summaryrefslogblamecommitdiff
path: root/sources/scalac/transformer/TypesAsValuesPhase.java
blob: e3ec60af485f3cb223f3b0c58379065ba25ff7c5 (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                          


                              
                              


                                 
                          
                               



                                 
                          
                           





                                 

                         
                         




                            
                             
 




















                                                                      

                                                                 
 
                                               
                                             
 



                                                               
                                                             
                      


                                                                
                      













                                                                               




                                                        
                                                    
                                                              
 


                                              

                                         
                                                      
 
                                                                            
 

                                                                          





                                                                      
 














                                                                
 
                                                          

                                                          
                                                                


       
                                                                   
       






                                                                           
                                                  








                                                          

                                                                
 



                                                                           
                                                                           





                                                                      
                                                                  




                                                       


                                                                          









                                                          

                                                                    
 

                                                         
                                                                      










                                                              
 







                                                                              
 

                                                              
                            
                                                          

                                                                           

                                                 



                                                                      

                                             
                                       



                                                           
             
 
                                           


                                                         
             
 
                                                                        


                                              

     










                                                                        
         

                     

     









                                                              


                                         

     
                                                         

                                              
                               
                                                  

                                  


                                                               

                                                      
                                                         
 

                                                                             
                                                                            




                                                                          





                                                                       
                                                 
                                                                         








                                                                      

                                                    

     

                                             

     
                                                             







                                              

                                                             
 
                                                  






                                                                           
                 
                                                                              
 
                                           

                                                                           
                                                  
                                                       


                                                   
 
                                                                           


                                                                    






                                                                           
 



                                                            








                                                                 



                                                       







                                                                  
                                                                             
                                     
                                                                 








                                                                           

                                                                          


                                                                       
                                                 




                                                                        
                                                                  

                                                            
                                          

                                                                



                                                   
                                                           
                                                                  


                                                                     

                                                            
                                          

                                                            
                                                    



                                                                      
















                                                                  





                                             

































                                                                               















                                                                     

















                                                                            
                                                                              






















                                                                            



































                                                                            
                                                              
                                                            
 


                                                  
                                                                                

                                     

                                                 

                                                 


                                          
                                                    


                                                                          











































































                                                                               
                                               
                                                                               
             















                                                                           
 



                                                                                
 




                                                                   











                                                                            
                                                                  






















                                                                                





                                                                   
                                                                  


                                                                         

         
           

                                                                   
           
                                            
                         


                                                          

                                    





                                                         



                                                                      











                                                          

















                                                                       


                                                        
                                                                            

                                            
                                                          
 



                                                            
                                                     



                                                                              
                                          
                                           
                                                              
                                              
                                                

                                                               
                        

                                                                    
                 
 
                                                  
                                                            
 


                                          


                                                                     
                    
                                                 


             











                                                                               
                                                    


                                                                            
                                                         

                                                             

         






                                                                              
                                                              

                                                                              
                                                                  











                                                                   

                                                                          
                                                        




                                                                           





                                                                               
                                                                           
                                                                        









                                                                               
                                                                           





































                                                                     










                                                              
                                                                 
                                                                      
                                        
                                                                          
 

                                                     
 

                                                                    

                                                                 




                                                                              
 
                                                                  
                                                                
                                                              
                                  

                                                                
                                                            
 
                                                            
                                                   

                                                                           
                                                       
                         
                                                           



                     




                                                                            


                                                                          

             
                                 

         
                                                            





                                                                              
             
                            

         

                                                                     
                                                    
                                                                     
                                                          

                                                            

                             
                                                                  

         
                                                              

                                                  

                              

                                                       




                                                                              


                                                          



                                           










                                                              






                                  













































                                                                           
                         


                                                                        
                                               
                                                          

                                               


                                                                          
             

                                              


         










                                                                          
                                   

                                                                     








                                                                       

     
/*     ____ ____  ____ ____  ______                                     *\
**    / __// __ \/ __// __ \/ ____/    SOcos COmpiles Scala             **
**  __\_ \/ /_/ / /__/ /_/ /\_ \       (c) 2002, LAMP/EPFL              **
** /_____/\____/\___/\____/____/                                        **
\*                                                                      */

// $Id$

package scalac.transformer;

import scalac.Global;
import scalac.Phase;
import scalac.PhaseDescriptor;
import scalac.CompilationUnit;
import scalac.symtab.Definitions;
import scalac.symtab.Scope;
import scalac.symtab.Symbol;
import scalac.symtab.Type;
import scalac.symtab.Modifiers;
import scalac.atree.AConstant;
import scalac.ast.Transformer;
import scalac.ast.GenTransformer;
import scalac.ast.Tree;
import scalac.ast.TreeGen;
import scalac.ast.TreeList;
import scalac.backend.Primitives;

import scalac.util.Name;
import scalac.util.Names;
import scalac.util.Debug;

import java.util.Map;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Arrays;
import java.util.Collections;

/**
 * Turn types into values by applying the following transformations:
 *
 * - For all type member T of all classes, add an accessor method
 *   T$type returnining the type as a value (the accessor is abstract
 *   if the type member is abstract).
 *
 * - For all polymorphic methods/constructors, add a value parameter
 *   for each type parameter.
 *
 * - Add a method getType to every class, to obtain its type as a
 *   value.
 *
 * - Transform all type expressions into value expressions: type
 *   application is turned into value application, type selection into
 *   value selection, and so on.
 *
 * @author Michel Schinz
 * @version 1.0
 */

// TODO use a constant instead of generating empty arrays all the
// time.

public class TypesAsValuesPhase extends Phase {
    private final GenTransformer transformer;

    /**
     * The list of members to add to a given class (either type
     * accessors or instantiation methods).
     */
    private final HashMap/*<Symbol,Symbol[]>*/ membersToAdd =
        new HashMap();

    /** The list of parameters to add to a given method. */
    private final HashMap/*<Symbol,List<Symbol>>*/ paramsToAdd =
        new HashMap();

    /** The accessor method corresponding to a given type (or class) member. */
    private final HashMap/*<Symbol, Symbol>*/ typeAccessor =
        new HashMap();

    /** The instanciation method corresponding to a given class. */
    private final HashMap/*<Symbol,Symbol>*/ instantiator =
        new HashMap();

    /** The class constructor corresponding to a given class. */
    private final HashMap/*<Symbol,Symbol>*/ classInitialiser =
        new HashMap();

    private final HashMap/*<Symbol,Symbol>*/ tConstructor =
        new HashMap();

    private final Definitions defs = global.definitions;
    private final Primitives prims = global.primitives;

    private final Type.MethodType typeAccessorType =
        new Type.MethodType(new Symbol[]{}, defs.TYPE_TYPE());

    private final Symbol ARRAY_CONSTRUCTOR =
        defs.ARRAY_CLASS.primaryConstructor();

    private final TEnv EENV = new TEnv();

    private final Map/*<Symbol, Symbol>*/ predefTypes;

    private HashMap/*<Symbol, Ancestor[][]>*/ ancestorCache = new HashMap();

    public TypesAsValuesPhase(Global global, PhaseDescriptor descriptor) {
        super(global, descriptor);
        // If RTT are disabled, use a minimal transformer which simply
        // replaces [ia]sInstanceOf with their erased counterpart.
        if (global.runTimeTypes)
            transformer = new TV_Transformer(global);
        else
            transformer = new TV_MiniTransformer(global);

        predefTypes = new HashMap();
        predefTypes.put(defs.DOUBLE_CLASS,  defs.RTT_DOUBLE());
        predefTypes.put(defs.FLOAT_CLASS,   defs.RTT_FLOAT());
        predefTypes.put(defs.LONG_CLASS,    defs.RTT_LONG());
        predefTypes.put(defs.INT_CLASS,     defs.RTT_INT());
        predefTypes.put(defs.SHORT_CLASS,   defs.RTT_SHORT());
        predefTypes.put(defs.CHAR_CLASS,    defs.RTT_CHAR());
        predefTypes.put(defs.BYTE_CLASS,    defs.RTT_BYTE());
        predefTypes.put(defs.BOOLEAN_CLASS, defs.RTT_BOOLEAN());
        predefTypes.put(defs.UNIT_CLASS,    defs.RTT_UNIT());

        predefTypes.put(defs.ANY_CLASS,     defs.RTT_ANY());
        predefTypes.put(defs.ANYVAL_CLASS,  defs.RTT_ANYVAL());
        predefTypes.put(defs.ALLREF_CLASS,  defs.RTT_ALLREF());
        predefTypes.put(defs.ALL_CLASS,     defs.RTT_ALL());

        membersToAdd.put(defs.ARRAY_CLASS, new Symbol[0]);
        paramsToAdd.put(ARRAY_CONSTRUCTOR, new Symbol[0]);

        ancestorCache.put(defs.OBJECT_CLASS, new Ancestor[0][]);
    }

    /**
     * Return the symbol of the accessor for the given type symbol.
     */
    private Symbol getAccessorSym(Symbol typeSym) {
        assert typeSym.isType();
        Symbol accessorSym = (Symbol)typeAccessor.get(typeSym);
        if (accessorSym == null) {
            accessorSym = typeSym.owner().newVariable(typeSym.pos,
                                                      typeSym.flags,
                                                      Names.TYPE(typeSym));
            accessorSym.setInfo(defs.TYPE_TYPE());
            typeAccessor.put(typeSym, accessorSym);
        }
        return accessorSym;
    }

    private Symbol getInstMethSym(Symbol classSym) {
        Symbol imSym = (Symbol)instantiator.get(classSym);
        if (imSym == null) {
            int pos = classSym.pos;
            boolean isStatic = !isNestedClass(classSym);
            Name imName = Names.INSTANTIATE(classSym, isStatic);

            int flags = classSym.isAbstractType() ? Modifiers.DEFERRED : 0;

            imSym = isStatic
                ? classSym.newStaticMethod(pos, 0, imName)
                : classSym.owner().newMethodOrFunction(pos, flags, imName);

            // TODO special case for monomorphic instantiations
            Symbol[] argTypes;
            if (true || classSym.typeParams().length > 0) {
                Symbol typesP =
                    imSym.newVParam(pos, 0, Name.fromString("types"));
                typesP.setInfo(defs.ARRAY_TYPE(defs.TYPE_TYPE()));
                argTypes = new Symbol[]{ typesP };
            } else
                argTypes = Symbol.EMPTY_ARRAY;

            imSym.setInfo(new Type.MethodType(argTypes,
                                              isStatic
                                              ? defs.SCALACLASSTYPE_TYPE()
                                              : defs.TYPE_TYPE()));

            instantiator.put(classSym, imSym);
        }
        return imSym;
    }

    private Symbol getTConstructorSym(Symbol classSym) {
        Symbol tcSym = (Symbol)tConstructor.get(classSym);
        if (tcSym == null) {
            int pos = classSym.pos;
            boolean isStatic = !isNestedClass(classSym);
            Name tcName = Names.TYPECONSTRUCTOR(classSym, isStatic);

            tcSym = isStatic
                ? classSym.newStaticField(pos, 0, tcName)
                : classSym.owner().newFieldOrVariable(pos, 0, tcName);
            tcSym.setInfo(defs.TYPECONSTRUCTOR_TYPE());

            tConstructor.put(classSym, tcSym);
        }
        return tcSym;
    }

    private Symbol getClassInitSym(Symbol classSym) {
        Symbol ciSym = (Symbol)classInitialiser.get(classSym);
        if (ciSym == null) {
            int pos = classSym.pos;

            ciSym = classSym.newStaticMethod(pos, 0, Names.CLASS_CONSTRUCTOR);
            ciSym.setInfo(new Type.MethodType(Symbol.EMPTY_ARRAY,
                                              defs.UNIT_TYPE()));

            classInitialiser.put(classSym, ciSym);
        }
        return ciSym;
    }

    private Symbol[] membersToAdd(Symbol classSym) {
        Symbol[] toAdd = (Symbol[])membersToAdd.get(classSym);
        if (toAdd == null) {
            HashSet seenMembers = new HashSet(); // [HACK]
            ArrayList toAddL = new ArrayList();
            Scope.SymbolIterator membersIt = classSym.members().iterator();
            while (membersIt.hasNext()) {
                Symbol member = membersIt.next();
                // [HACK] work around a bug in the analyzer, which
                // doesn't add the module class to its owner's members
                if (member.isModule())
                    member = member.moduleClass();
                if (!seenMembers.add(member))
                    continue;
                if (member.isClass()) {
                    toAddL.add(getTConstructorSym(member));
                    toAddL.add(getInstMethSym(member));
                } else if (member.isType())
                    toAddL.add(getInstMethSym(member));
            }

            if (!isNestedClass(classSym)) {
                toAddL.add(getTConstructorSym(classSym));
                toAddL.add(getClassInitSym(classSym));
                toAddL.add(getInstMethSym(classSym));
            }

            toAdd = (Symbol[])toAddL.toArray(new Symbol[toAddL.size()]);
            membersToAdd.put(classSym, toAdd);
        }
        return toAdd;
    }

    private Symbol[] paramsToAdd(Symbol methSym) {
        Symbol[] toAdd = (Symbol[])paramsToAdd.get(methSym);
        if (toAdd == null) {
            Symbol[] tparams = methSym.typeParams();

            ArrayList toAddL = new ArrayList();
            for (int i = 0; i < tparams.length; ++i)
                toAddL.add(getAccessorSym(tparams[i]));

            toAdd = (Symbol[])toAddL.toArray(new Symbol[toAddL.size()]);
            paramsToAdd.put(methSym, toAdd);
        }

        return toAdd;
    }

    /**
     * Return true iff the given symbol is not a polymorphic
     * primitive, which shouldn't get type parameters as value
     * parameters.
     */
    private boolean monoPrimitive(Symbol sym) {
        return sym.isJava()
            || sym == ARRAY_CONSTRUCTOR
            || sym == defs.OBJECT_SYNCHRONIZED
            || sym == defs.ANY_IS
            || sym == defs.ANY_IS_ERASED
            || sym == defs.ANY_AS
            || sym == defs.ANY_AS_ERASED;
    }

    public Type transformInfo(Symbol symbol, Type type) {
        if (!global.runTimeTypes) return type;

        if (symbol.isClass()) {
            Symbol[] toAdd = membersToAdd(symbol);

            if (toAdd.length == 0)
                return type;
            else {
                Scope newMembers = new Scope(symbol.members());

                for (int i = 0; i < toAdd.length; ++i)
                    newMembers.enterOrOverload(toAdd[i]);

                return Type.compoundType(type.parents(), newMembers, symbol);
            }
        } else if (type.typeParams().length > 0 && !monoPrimitive(symbol)) {
            // Polymorphic method/constructor:
            // - add a value parameter for every type parameter.
            switch (type) {
            case PolyType(Symbol[] tparams, // :
                          Type.MethodType(Symbol[] vparams, Type result)):
                List newVParams =
                    new LinkedList(Arrays.asList(paramsToAdd(symbol)));
                newVParams.addAll(Arrays.asList(vparams));
                Symbol[] newVParamsA = (Symbol[])
                    newVParams.toArray(new Symbol[newVParams.size()]);

                return new Type.PolyType(tparams,
                                         new Type.MethodType(newVParamsA,
                                                             result));

            default:
                throw Debug.abort("unexpected type: ", type);
            }
        } else
            return type;
    }

    private boolean isNestedClass(Symbol classSym) {
        return !classSym.owner().isPackageClass();
    }

    public void apply(CompilationUnit unit) {
        transformer.apply(unit);
    }

    private class TV_Transformer extends TV_MiniTransformer {
        private Symbol currentOwner;

        public TV_Transformer(Global global) {
            super(global);
        }

        public Tree transform(Tree tree) {
            switch (tree) {
            case ClassDef(_, _, _, _, _, Tree.Template impl):
                Symbol clsSym = tree.symbol();

                TreeList newBody = new TreeList();
                if (!isNestedClass(clsSym)) {
                    Symbol tcSym = getTConstructorSym(clsSym);
                    newBody.append(tConstructorVal(clsSym, tcSym));
                    Symbol ciSym = getClassInitSym(clsSym);
                    newBody.append(classInitialiser(clsSym, ciSym, tcSym));
                    Symbol imSym = getInstMethSym(clsSym);
                    newBody.append(instantiatorBody(clsSym, imSym));
                }
                newBody.append(transformStatements(impl.body, impl.symbol()));

                return gen.ClassDef(clsSym,
                                    transform(impl.parents,
                                              clsSym.primaryConstructor()),
                                    impl.symbol(),
                                    newBody.toArray());

            case DefDef(_, _, _, _, _, Tree rhs):
                Symbol symbol = getSymbolFor(tree);

                if (symbol.name == Names.getType && symbol.isSynthetic()) {
                    // Correct the body of the getType method which,
                    // until now, was a placeholder (introduced by
                    // RefCheck).
                    return gen.DefDef(symbol,
                                      scalaClassType(symbol.pos,
                                                     symbol.owner().type(),
                                                     symbol,
                                                     EENV));
                } else
                    return gen.DefDef(symbol, transform(rhs, symbol));

            case Block(Tree[] stats, Tree value):
                return gen.Block(tree.pos,
                                 transformStatements(stats),
                                 transform(value));

            case ValDef(_, _, Tree tpe, Literal(AConstant.ZERO)):
                // transform default values:
                //   val x: T = _
                // becomes
                //   val x: T = asValue(T).defaultValue()
                Symbol symbol = getSymbolFor(tree);
                Tree defaultValue =
                    gen.mkRef(tree.pos,
                              typeAsValue(tree.pos,
                                          tpe.type,
                                          currentOwner,
                                          EENV),
                              defs.TYPE_DEFAULTVALUE());
                Tree rhs = gen.mkApply__(tree.pos, defaultValue);
                return gen.ValDef(symbol, rhs);

            case ValDef(_, _, _, Tree rhs):
                Symbol symbol = getSymbolFor(tree);
                return gen.ValDef(symbol, transform(rhs, symbol));

            case New(Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs)):
                // TODO still needed?
                if (fun.symbol() == ARRAY_CONSTRUCTOR && false) {
                    // Transform array creations:
                    //   new Array[T](size)
                    // becomes
                    //   asValue(T).newArray[T](size)
                    assert targs.length == 1;
                    assert vargs.length == 1;
                    Tree newArrayfun = gen.mkRef(tree.pos,
                                                 typeAsValue(targs[0].pos,
                                                             targs[0].type,
                                                             currentOwner,
                                                             EENV),
                                                 defs.TYPE_NEWARRAY());
                    return gen.mkApplyTV(newArrayfun, targs, vargs);
                } else
                    return super.transform(tree);

            case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs):
                Symbol funSym = fun.symbol();

                if (funSym == defs.ANY_IS) {
                    assert targs.length == 1 && vargs.length == 0;
                    Type type = targs[0].type;
                    Tree expr = transform(qualifierOf(fun));
                    return isTrivial(type)
                        ? super.transform(tree)
                        : genInstanceTest(tree.pos, expr, type);
                } else if (funSym == defs.ANY_AS) {
                    // Transform instance tests:
                    //   e.asInstanceOf[T]
                    // becomes:
                    //   asValue(T).cast(e).asInstanceOf[T]
                    // unless T is a trivial type for which a Java
                    // instance test is sufficient, in which case the
                    // expression is left as is.
                    assert targs.length == 1 && vargs.length == 0;
                    Type type = targs[0].type;
                    Tree expr = transform(qualifierOf(fun));
                    return isTrivial(type)
                        ? super.transform(tree)
                        : genTypeCast(tree.pos, expr, type);
                } else if (!monoPrimitive(funSym)) {
                    // Transform applications to pass types as values:
                    //   f[T1, ...](v1, ...)
                    // becomes
                    //   f[T1, ...](asValue(T1), ..., v1, ...)
                    Tree[] newVArgs = transform(vargs);
                    Tree[] finalVArgs =
                        new Tree[newVArgs.length + targs.length];
                    for (int i = 0; i < targs.length; ++i)
                        finalVArgs[i] = typeAsValue(targs[i].pos,
                                                    targs[i].type,
                                                    currentOwner,
                                                    EENV);
                    System.arraycopy(newVArgs, 0,
                                     finalVArgs, targs.length,
                                     newVArgs.length);
                    return gen.mkApplyTV(tree.pos,
                                         transform(fun),
                                         targs,
                                         finalVArgs);
                } else
                    return super.transform(tree);

            default:
                return super.transform(tree);
            }
        }

        private Tree[] transformStatements(Tree[] stats) {
            ArrayList newStats = new ArrayList();
            int beginIdx = 0;
            for (int i = 0; i < stats.length; ++i) {
                Tree stat = stats[i];
                switch (stat) {
                case ClassDef(_, _, _, _, _, Tree.Template impl):
                    Symbol clsSym = stat.symbol();
                    Symbol tcSym = getTConstructorSym(clsSym);
                    newStats.add(beginIdx++, tConstructorVal(clsSym, tcSym));
                    Symbol insSym = getInstMethSym(clsSym);
                    newStats.add(instantiatorBody(clsSym, insSym));
                    break;

                case AbsTypeDef(_, _, _, _):
                case AliasTypeDef(_, _, _, _):
                    Symbol tpSym = stat.symbol();
                    Symbol insSym = getInstMethSym(tpSym);
                    newStats.add(typeAccessorBody(tpSym, insSym));
                    break;
                }
                newStats.add(transform(stat));
            }
            return (Tree[])newStats.toArray(new Tree[newStats.size()]);
        }

        private Tree[] transformStatements(Tree[] stats, Symbol currentOwner) {
            Symbol bkpOwner = this.currentOwner;
            this.currentOwner = currentOwner;
            Tree[] newStats = transformStatements(stats);
            this.currentOwner = bkpOwner;
            return newStats;
        }

        private Tree transform(Tree tree, Symbol currentOwner) {
            Symbol bkpOwner = this.currentOwner;
            this.currentOwner = currentOwner;
            Tree newTree = transform(tree);
            this.currentOwner = bkpOwner;
            return newTree;
        }

        private Tree[] transform(Tree[] trees, Symbol currentOwner) {
            Symbol bkpOwner = this.currentOwner;
            this.currentOwner = currentOwner;
            Tree[] newTrees = transform(trees);
            this.currentOwner = bkpOwner;
            return newTrees;
        }

        private int level(Symbol sym) {
            Symbol superClass = sym.parents()[0].symbol();
            assert superClass != Symbol.NONE : sym;
            if (superClass == defs.ANY_CLASS)
                return 0;
            else
                return 1 + level(superClass);
        }

        /**
         * Return a method giving access to the given type, as a
         * value.
         */
        private Tree.DefDef typeAccessorBody(Symbol typSym, Symbol accSym) {
            Tree rhs;
            if (typSym.isAbstractType())
                rhs = Tree.Empty;
            else if (typSym.isClass())
                rhs = scalaClassType(typSym.pos, typSym.type(), accSym, EENV);
            else {
                final Symbol[] vparams = accSym.valueParams();
                final int pos = accSym.pos;

                final HashMap varMap = new HashMap();
                Symbol[] tparams = typSym.typeParams();
                for (int i = 0; i < tparams.length; ++i)
                    varMap.put(tparams[i], new Integer(i));

                TEnv tEnv = new TEnv() {
                        public boolean definesVar(Symbol sym) {
                            return varMap.containsKey(sym);
                        }

                        public Tree treeForVar(Symbol sym) {
                            int idx = ((Integer)varMap.get(sym)).intValue();
                            Tree array = gen.mkLocalRef(pos, vparams[0]);
                            return gen.mkArrayGet(pos, array, idx);
                        }
                    };

                rhs = typeAsValue(typSym.pos, typSym.type(), accSym, tEnv);
            }
            return gen.DefDef(accSym, rhs);
        }

        private Tree tConstructorVal(Symbol clsSym, Symbol tcSym) {
            return gen.ValDef(tcSym,
                              tcSym.isStatic()
                              ? Tree.Empty
                              : tConstructorRHS(tcSym.pos, clsSym, tcSym));
        }

        private Tree classInitialiser(Symbol clsSym,
                                      Symbol ciSym,
                                      Symbol tcSym) {
            if (tcSym.isStatic()) {
                int pos = tcSym.pos;
                Tree rhs = tConstructorRHS(pos, clsSym, ciSym);
                Tree assign = gen.Assign(pos, gen.Ident(pos, tcSym), rhs);

                return gen.DefDef(ciSym, assign);
            } else
                return Tree.Empty;
        }

        private Tree tConstructorRHS(int pos, Symbol clsSym, Symbol owner) {
            int zCount = 0, mCount = 0, pCount = 0;
            Symbol[] tparams = clsSym.typeParams();

            for (int i = 0; i < tparams.length; ++i) {
                if ((tparams[i].flags & Modifiers.COVARIANT) != 0)
                    ++pCount;
                else if ((tparams[i].flags & Modifiers.CONTRAVARIANT) != 0)
                    ++mCount;
                else
                    ++zCount;
            }

            Ancestor[][] ancestors = computeAncestors(clsSym);
            int[] ancestorCode = getAncestorCode(ancestors);

            Tree outer = isNestedClass(clsSym)
                ? (clsSym.owner().isClass()
                   ? gen.This(pos, clsSym.owner())
                   : gen.mkGlobalRef(pos, defs.TYPECONSTRUCTOR_FUNCTIONOUTER()))
                : gen.mkNullLit(pos);

            Tree[] tcArgs = new Tree[] {
                gen.mkIntLit(pos, level(clsSym)),
                gen.mkSymbolNameLit(pos, clsSym),
                outer,
                gen.mkIntLit(pos, zCount),
                gen.mkIntLit(pos, mCount),
                gen.mkIntLit(pos, pCount),
                gen.mkIntLit(pos, ancestors.length),
                ancestorCode.length == 0
                ? gen.mkNullLit(pos)
                : mkNewIntLitArray(pos, getAncestorCode(ancestors), owner)
            };

            Symbol tcConst = defs.TYPECONSTRUCTOR_CLASS.primaryConstructor();
            Tree tcCall =
                gen.mkApply_V(pos, gen.mkGlobalRef(pos, tcConst), tcArgs);
            return gen.New(pos, tcCall);
        }

        private Tree mkNewIntLitArray(int pos, int[] values, Symbol owner) {
            Tree[] intLits = new Tree[values.length];
            for (int i = 0; i < values.length; ++i)
                intLits[i] = gen.mkIntLit(pos, values[i]);
            return gen.mkNewArray(pos, defs.INT_TYPE(), intLits, owner);
        }

        /**
         * Return a method to instantiate the given type.
         */
        private Tree.DefDef instantiatorBody(Symbol clsSym, Symbol insSym) {
            // TODO fix flags for all symbols below
            final int pos = clsSym.pos;
            final Symbol[] vparams = insSym.valueParams();

            Tree[] body = new Tree[2];

            // Generate call to "getInstantiation" method of
            // constructor.
            Tree getInstFun =
                gen.Select(pos,
                           gen.mkLocalRef(pos, getTConstructorSym(clsSym)),
                           defs.TYPECONSTRUCTOR_GETINSTANTIATION());

            Tree[] getInstArgs = new Tree[]{ gen.mkLocalRef(pos, vparams[0]) };

            Symbol instVal =
                insSym.newVariable(pos, 0, Name.fromString("inst"));
            instVal.setInfo(defs.SCALACLASSTYPE_TYPE());

            Tree instValDef =
                gen.ValDef(instVal,
                           gen.mkApply_V(pos, getInstFun, getInstArgs));

            // Generate test to see if a call to "instantiate" is
            // necessary.
            Tree cond =
                gen.mkApply_V(pos,
                              gen.Select(pos,
                                         gen.mkLocalRef(pos, instVal),
                                         defs.ANY_BANGEQ),
                              new Tree[] { gen.mkNullLit(pos) });
            Tree thenP = gen.mkLocalRef(pos, instVal);

            final HashMap varMap = new HashMap();
            Symbol[] tparams = clsSym.typeParams();
            for (int i = 0; i < tparams.length; ++i)
                varMap.put(tparams[i], new Integer(i));

            // Type environment mapping the type parameters of the
            // class to their corresponding element in the "types"
            // array passed to this instantiator.
            TEnv tEnv = new TEnv() {
                    public boolean definesVar(Symbol sym) {
                        return varMap.containsKey(sym);
                    }

                    public Tree treeForVar(Symbol sym) {
                        int idx = ((Integer)varMap.get(sym)).intValue();
                        Tree array = gen.mkLocalRef(pos, vparams[0]);
                        return gen.mkArrayGet(pos, array, idx);
                    }
                };

            Type[] parents = clsSym.parents();
            TreeList parentTypes = new TreeList();
            for (int i = 0; i < parents.length; ++i) {
                Type parent = parents[i];
                if (!isStronglyTrivial(parent))
                    parentTypes.append(typeAsValue(pos, parent, insSym, tEnv));
            }
            boolean emptyParents = (parentTypes.length() == 0);
            Tree parentsArray = emptyParents
                ? gen.mkGlobalRef(pos, defs.SCALACLASSTYPE_EMPTYARRAY())
                : gen.mkNewArray(pos,
                                 defs.SCALACLASSTYPE_TYPE(),
                                 parentTypes.toArray(),
                                 insSym);
            Tree instFun =
                gen.Select(pos,
                           gen.mkLocalRef(pos, getTConstructorSym(clsSym)),
                           defs.TYPECONSTRUCTOR_INSTANTIATE());
            Tree[] instArgs = new Tree[] {
                gen.mkLocalRef(pos, vparams[0]),
                emptyParents ? parentsArray : gen.mkNullLit(pos)
            };
            Tree instCall = gen.mkApply_V(pos, instFun, instArgs);

            Tree elseP;
            if (!emptyParents) {
                Tree setParentsFun =
                    gen.Select(pos, instCall, defs.SCALACLASSTYPE_SETPARENTS());

                elseP = gen.mkApply_V(pos,
                                      setParentsFun,
                                      new Tree[] { parentsArray });
            } else
                elseP = instCall;

            Tree ifExpr =
                gen.If(pos, cond, thenP, elseP, defs.SCALACLASSTYPE_TYPE());

            return gen.DefDef(insSym, gen.mkBlock(pos, instValDef, ifExpr));
        }

        /**
         * Generate code to test if the given expression is an
         * instance of the given type.
         */
        private Tree genInstanceTest(int pos, Tree expr, Type tp) {
            Tree tpVal = typeAsValue(pos, tp, currentOwner, EENV);

            if (isKnowClassType(tp) && isLocalIdent(expr)) {
                Symbol sym = expr.symbol();
                Tree cheapTest =
                    gen.mkIsInstanceOf(pos, gen.mkLocalRef(pos, sym), tp, true);
                Symbol weakIsInst = defs.SCALACLASSTYPE_WEAKISINSTANCE();
                Tree scalaTpVal = gen.mkAsInstanceOf(pos,
                                                     tpVal,
                                                     defs.SCALACLASSTYPE_TYPE(),
                                                     true);
                Tree expensiveTest =
                    gen.mkApply_V(pos,
                                  gen.Select(pos, scalaTpVal, weakIsInst),
                                  new Tree[] { expr });
                return gen.mkApply_V(pos,
                                     gen.Select(pos,
                                                cheapTest,
                                                defs.BOOLEAN_AND()),
                                     new Tree[] { expensiveTest });
            } else {
                Tree fun = gen.Select(pos, tpVal, defs.TYPE_ISINSTANCE());
                return gen.mkApply_V(pos, fun, new Tree[] { expr });
            }
        }

        /**
         * Generate code to cast the given value to the given type.
         */
        private Tree genTypeCast(int pos, Tree expr, Type tp) {
            Tree tpVal = typeAsValue(pos, tp, currentOwner, EENV);
            Tree fun = gen.Select(pos, tpVal, defs.TYPE_CAST());
            Tree castCall = gen.mkApply_V(pos, fun, new Tree[] { expr });
            return gen.mkAsInstanceOf(pos, castCall, tp);
        }

        /**
         * Return true iff the given type is trivial, that is if it
         * has neither a prefix, nor type parameters.
         */
        private boolean isTrivial(Type tp) {
            switch (tp) {
            case TypeRef(_, Symbol sym, Type[] args):
                return sym.isStatic() && args.length == 0;
            case SingleType(_, _):
            case ThisType(_):
            case CompoundType(_, _):
                return false;
            default:
                throw Debug.abort("unexpected type", tp);
            }
        }

        /**
         * Return true iff the given type is strongly trivial, that is
         * if it and all its ancestors are trivial.
         */
        private boolean isStronglyTrivial(Type tp) {
            if (isTrivial(tp)) {
                Type[] parents = tp.parents();
                for (int i = 0; i < parents.length; ++i) {
                    if (!isStronglyTrivial(parents[i]))
                        return false;
                }
                return true;
            } else
                return false;
        }

        private boolean isKnowClassType(Type tp) {
            switch (tp) {
            case TypeRef(_, Symbol sym, _):
                return (sym != defs.ARRAY_CLASS) && !sym.isParameter();
            default:
                return false;
            }
        }

        private boolean isLocalIdent(Tree tree) {
            switch (tree) {
            case Ident(_):
                return tree.symbol().isLocal();
            default:
                return false;
            }
        }

        /**
         * Transform a type into a tree representing it.
         */
        private Tree typeAsValue(int pos, Type tp, Symbol owner, TEnv env) {
            switch (tp) {
            case ConstantType(Type base, _):
                return typeAsValue(pos, base, owner, env);

            case TypeRef(Type pre, Symbol sym, Type[] args):
                if (env.definesVar(sym)) {
                    assert args.length == 0;
                    return env.treeForVar(sym);
                } else if (sym == defs.ARRAY_CLASS) {
                    assert args.length == 1;
                    return arrayType(pos, sym, args[0], owner, env);
                } else if (predefTypes.containsKey(sym)) {
                    return gen.mkGlobalRef(pos, (Symbol)predefTypes.get(sym));
                } else if (sym.isJava()) {
                    assert args.length <= 1
                        : Debug.show(sym) + " " + args.length;
                    return javaType(pos, sym);
                } else if (!sym.isParameter()) {
                    // Reference to a "global" type.
                    return scalaClassType(pos, tp, owner, env);
                } else {
                    assert !isValuePrefix(pre) : tp;
                    return gen.mkLocalRef(pos, getAccessorSym(sym));
                }

            case SingleType(Type pre, Symbol sym):
                return singleType(pos, (Type.SingleType)tp);

            case ThisType(Symbol sym):
                return thisType(pos, sym);

            case CompoundType(Type[] parts, Scope members):
                return compoundType(pos, parts, members, owner, env);

            default:
                throw Debug.abortIllegalCase(tp);
            }
        }

        private Tree arrayType(int pos,
                               Symbol sym,
                               Type elemType,
                               Symbol owner,
                               TEnv env) {
            Tree constr =
                gen.mkPrimaryConstructorGlobalRef(pos,
                                                  defs.JAVAREFARRAYTYPE_CLASS);
            Tree[] args = new Tree[]{ typeAsValue(pos, elemType, owner, env) };
            return gen.New(pos, gen.mkApply_V(constr, args));
        }

        private Tree javaType(int pos, Symbol sym) {
            Tree constr =
                gen.mkPrimaryConstructorGlobalRef(pos,
                                                  defs.JAVACLASSTYPE_CLASS);
            Tree nameLit = gen.mkSymbolNameLit(pos, sym);
            Tree[] args = new Tree[] { nameLit };
            return gen.New(pos, gen.mkApply_V(constr, args));
        }

        private Tree thisType(int pos, Symbol sym) {
            Tree constr =
                gen.mkPrimaryConstructorGlobalRef(pos, defs.SINGLETYPE_CLASS);
            Tree[] args = new Tree[] { gen.This(pos, sym) };
            return gen.New(pos, gen.mkApply_V(constr, args));
        }

        private Tree singleType(int pos, Type.SingleType tp) {
            Tree constr =
                gen.mkPrimaryConstructorGlobalRef(pos, defs.SINGLETYPE_CLASS);
            Tree[] args = new Tree[] { gen.mkQualifier(pos, tp) };
            return gen.New(pos, gen.mkApply_V(constr, args));
        }

        private Tree compoundType(int pos,
                                  Type[] parts,
                                  Scope members,
                                  Symbol owner,
                                  TEnv env) {
            Tree[] partsT = new Tree[parts.length];
            for (int i = 0; i < parts.length; ++i)
                partsT[i] = typeAsValue(pos, parts[i], owner, env);

            Tree[] constrArgs = new Tree[] {
                gen.mkNewArray(pos, defs.CLASSTYPE_TYPE(), partsT, owner),
                gen.mkBooleanLit(pos, members.isEmpty())
            };
            Tree constr =
                gen.mkPrimaryConstructorGlobalRef(pos,
                                                  defs.COMPOUNDTYPE_CLASS);
            return gen.New(pos, gen.mkApply_V(constr, constrArgs));
        }

        private Tree scalaClassType(int pos, Type tp, Symbol owner, TEnv env) {
            switch (tp) {
            case TypeRef(Type pre, Symbol sym, Type[] args):
                Symbol insSym = getInstMethSym(sym);
                Tree preFun = (isNestedClass(sym) && sym.owner().isClass())
                    ? gen.Select(pos, gen.mkQualifier(pos, pre), insSym)
                    : gen.Ident(pos, insSym);

                // TODO special case for monomorphic cases
                Tree[] insArgs;
                if (true || args.length > 0) {
                    Tree[] elems = new Tree[args.length];
                    int[] perm = typeParamsPermutation(sym.typeParams());
                    for (int i = 0; i < args.length; ++i)
                        elems[i] = typeAsValue(pos, args[perm[i]], owner, env);
                    insArgs = new Tree[] {
                        gen.mkNewArray(pos, defs.TYPE_TYPE(), elems, owner)
                    };
                } else
                    insArgs = Tree.EMPTY_ARRAY;

                return gen.mkApply_V(pos, preFun, insArgs);

            default:
                throw Debug.abort("unexpected type: ", tp);
            }
        }

        private final int VARIANT =
            Modifiers.COVARIANT | Modifiers.CONTRAVARIANT;

        /**
         * Compute the (unique) permutation which puts all invariant
         * type parameters first, followed by the contravariant ones,
         * then the covariants, preserving the relative ordering of
         * arguments with same variance.
         */
        private int[] typeParamsPermutation(Symbol[] params) {
            int[] tparamsPerm = new int[params.length];
            int permIdx = 0;

            for (int i = 0; i < params.length; ++i)
                if ((params[i].flags & VARIANT) == 0)
                    tparamsPerm[permIdx++] = i;
            for (int i = 0; i < params.length; ++i)
                if ((params[i].flags & Modifiers.CONTRAVARIANT) != 0)
                    tparamsPerm[permIdx++] = i;
            for (int i = 0; i < params.length; ++i)
                if ((params[i].flags & Modifiers.COVARIANT) != 0)
                    tparamsPerm[permIdx++] = i;
            assert permIdx == tparamsPerm.length;

            return tparamsPerm;
        }

        private boolean isValuePrefix(Type pre) {
            switch (pre) {
            case ThisType(Symbol clazz):
                return !(clazz.isPackage() || clazz.isNone());
            case NoPrefix:
                return false;
            default:
                return true;
            }
        }

        private Ancestor[][] computeAncestors0(Symbol classSym) {
            Symbol[] nstParents = notStronglyTrivialParents(classSym);
            int level = level(classSym);
            ArrayList/*<Ancestor>*/[] ancestor = new ArrayList[level + 1];

            for (int l = 0; l < ancestor.length; ++l)
                ancestor[l] = new ArrayList();

            if (!isTrivial(classSym.type()))
                ancestor[level].add(new Ancestor(classSym, -1, -1));

            // Go over parents from left to right and add missing
            // ancestors to the set, remembering where they come from.
            for (int p = 0; p < nstParents.length; ++p) {
                Symbol parentSymbol = nstParents[p];
                Ancestor[][] parentAncestors = computeAncestors(parentSymbol);
                assert parentAncestors.length <= ancestor.length;

                for (int l = 0; l < parentAncestors.length; ++l) {
                    ArrayList/*<Ancestor>*/ myRow = ancestor[l];
                    Ancestor[] parentRow = parentAncestors[l];
                    parentRowLoop:
                    for (int i = 0; i < parentRow.length; ++i) {
                        Symbol sym = parentRow[i].symbol;
                        assert !isTrivial(sym.type()) : sym;

                        Iterator myRowIt = myRow.iterator();
                        while (myRowIt.hasNext()) {
                            Ancestor myAncestor = (Ancestor)myRowIt.next();
                            if (myAncestor.symbol == sym)
                                continue parentRowLoop;
                        }
                        myRow.add(new Ancestor(sym, p, i));
                    }
                }
            }

            int ancestorsLen = ancestor.length;
            while (ancestorsLen > 0 && ancestor[ancestorsLen - 1].isEmpty())
                --ancestorsLen;

            Ancestor[][] finalAncestor = new Ancestor[ancestorsLen][];
            for (int i = 0; i < finalAncestor.length; ++i) {
                finalAncestor[i] = (Ancestor[])
                    ancestor[i].toArray(new Ancestor[ancestor[i].size()]);
            }

            return finalAncestor;
        }

        /** Return the non-trivial ancestors of the class */
        private Ancestor[][] computeAncestors(Symbol classSym) {
            Ancestor[][] ancestor = (Ancestor[][])ancestorCache.get(classSym);
            if (ancestor == null) {
                ancestor = computeAncestors0(classSym);
                ancestorCache.put(classSym, ancestor);
//                 debugPrintAncestor(classSym, ancestor);
            }
            return ancestor;
        }

        /** Return the parents which are not strongly trivial. */
        private Symbol[] notStronglyTrivialParents(Symbol classSym) {
            Type[] parentTypes = classSym.parents();
            ArrayList nstParents = new ArrayList(parentTypes.length);
            for (int i = 0; i < parentTypes.length; ++i) {
                if (!isStronglyTrivial(parentTypes[i]))
                    nstParents.add(parentTypes[i].symbol());
            }
            return (Symbol[])
                nstParents.toArray(new Symbol[nstParents.size()]);
        }

        private int[] getAncestorCode(Ancestor[][] ancestor) {
            ArrayList/*<Ancestor>*/[] prunedRows =
                new ArrayList[ancestor.length];

            int totalSize = 0;
            for (int l = 0; l < ancestor.length; ++l) {
                Ancestor[] row = ancestor[l];
                ArrayList/*<Ancestor>*/ prunedRow = new ArrayList(row.length);
                for (int i = 0; i < row.length; ++i) {
                    if (row[i].parentIndex > 0)
                        prunedRow.add(row[i]);
                }
                prunedRows[l] = prunedRow;
                if (!prunedRow.isEmpty())
                    totalSize += 2 + 2 * prunedRow.size();
            }

            int[] res = new int[totalSize];
            int i = 0;
            for (int l = 0; l < prunedRows.length; ++l) {
                ArrayList row = prunedRows[l];
                if (!row.isEmpty()) {
                    res[i++] = l;
                    res[i++] = row.size();
                    Iterator ancIt = row.iterator();
                    while (ancIt.hasNext()) {
                        Ancestor anc = (Ancestor)ancIt.next();
                        res[i++] = anc.parentIndex;
                        res[i++] = anc.position;
                    }
                }
            }
            assert i == totalSize;
            return res;
        }
    }

    /**
     * Minimalistic transformer, which simply transforms calls to
     * isInstanceOf/asInstanceOf by calls to their erased
     * counterparts. Used when full run time types are disabled.
     */
    private class TV_MiniTransformer extends GenTransformer {
        private final Definitions defs;

        public TV_MiniTransformer(Global global) {
            super(global);
            defs = global.definitions;
        }

        public Tree transform(Tree tree) {
            switch (tree) {
            case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs):
                Symbol funSym = fun.symbol();
                if (funSym == defs.ANY_IS || funSym == defs.ANY_AS) {
                    Symbol erasedSym = (funSym == defs.ANY_AS)
                        ? defs.ANY_AS_ERASED
                        : defs.ANY_IS_ERASED;
                    return gen.mkApplyTV(tree.pos,
                                         gen.Select(fun.pos,
                                                    qualifierOf(fun),
                                                    erasedSym),
                                         targs,
                                         vargs);
                } else
                    return super.transform(tree);

            default:
                return super.transform(tree);
            }
        }
    }

    /**
     * Extract qualifier from a tree, which must be a Select node.
     */
    private Tree qualifierOf(Tree tree) {
        switch (tree) {
        case Select(Tree qualifier, _): return qualifier;
        default: throw Debug.abort("cannot extract qualifier from ", tree);
        }
    }

    // Debugging function
    private void debugPrintAncestor(Symbol sym, Ancestor[][] ancestor) {
        System.out.println("ancestor for " + Debug.show(sym));
        for (int l = 0; l < ancestor.length; ++l) {
            System.out.print("  [" + l + "] ");
            for (int i = 0; i < ancestor[l].length; ++i) {
                if (i > 0)
                    System.out.print("      ");
                System.out.println(" " + Debug.show(ancestor[l][i].symbol)
                                   + "/par" + ancestor[l][i].parentIndex
                                   + "/pos" + ancestor[l][i].position);
            }
            if (ancestor[l].length == 0)
                System.out.println("<empty>");
        }
    }

    //////////////////////////////////////////////////////////////////////

    private static class TEnv {
        public boolean definesVar(Symbol sym) {
            return false;
        }
        public Tree treeForVar(Symbol sym) {
            throw Debug.abort("no tree for variable " + sym);
        }
    }

    private static class Ancestor {
        public static final Ancestor[] EMPTY_ARRAY = new Ancestor[0];

        public final Symbol symbol;
        public final int parentIndex;
        public final int position;

        public Ancestor(Symbol symbol, int parentIndex, int position) {
            this.symbol = symbol;
            this.parentIndex = parentIndex;
            this.position = position;
        }
    }
}