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










                                                                          

                          
                         

                         

                     
                    

                       
                             
                                

                                    

                              
                                 
                              





                                 
                                          

                            
                                        
                                
















                                                                              
                                                                  
 


                                                                              
                            
                             
                                                          









                                                                              







                                          


                           

                                  







                                                                              
                                                           

     
                                                                              







                                     



                                      






                                                               










                                                                          




                                                                    




                                                  






                                                                       
                                                                     


                                                  
                                                                                 







                                                                       



                                                                    
                                               
                                                                    
                                                                               
             
                                               










                                                                             



                                                                          
                                                                



                                                                  

                                                   
                                          


                                                                  
                                          



                                                                      
                                          






                                                                             

                                          
                                                                              
                                                                             
                                             

                                                  
                                                                             
                                                                        

                                                           


                                           


                                
                                                                 
 
                
                                         





                                                                              













                                                                          
























                                                                             


                                                                         










                                               








                                                        
                                                                         


                                

                                                    

                                                                  
                                                             
                                                            
                                                                       

                                                                       



                        

















                                                                      
                                                            
                                                                            
                                                            





                                            











                                                                           
                                                                     
                                                         
                                                  





                                                                 






                                                                         
                                                           

                                                                 




                                                                     

                                                                 

     
                                                                              

                                        




                                                                     
                                                             


                                                                   

                                                                      
                                






                                                              
                                                                   




                                                  
                                                                               


         




                                                                       



                                                                   
                                                    
         


                                                                               

                                                                               

                                                                            
                                                                         






                                                                    

                                                           
                                                                 



                                                             

                                                                         
                                
                                                                 



                                                                      

                                                                      
                                           
                                       
                                                                 



                                                                               

                                                                      
                                           
                                                                    
                                              
                                                                 


                                                                              




















































                                                              




























                                                                      













                                                                              











































                                                                               


                                                                              

                                                                                  







                                                                                 
                                                                  
                                          






                                                                         
                                                            


                                                
                                              

                                                    
                                  

                                                                               
                                               
                                                          
                                     

                                                            
                                                                          













                                                                                 
                                                              


                                                               


                                                            

     






















                                                                         




































                                                                               













                                                                                             


                                                                          



                                                                                                 


                                                                                           
                                                                                                    


                                                 






                                                             





                                                                
             
                                        

         




                                                            
                                                 














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

// $OldId: Erasure.java,v 1.48 2003/01/16 14:21:19 schinz Exp $
// $Id$

package scalac.transformer;

import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

import scalac.Global;
import scalac.Phase;
import scalac.Unit;
import scalac.ast.Tree;
import scalac.ast.Tree.Ident;
import scalac.ast.Tree.Template;
import scalac.ast.Tree.AbsTypeDef;
import scalac.ast.Tree.AliasTypeDef;
import scalac.ast.Tree.ValDef;
import scalac.ast.TreeList;
import scalac.ast.GenTransformer;
import scalac.atree.AConstant;
import scalac.symtab.Definitions;
import scalac.symtab.Kinds;
import scalac.symtab.Type;
import scalac.symtab.TypeTags;
import scalac.symtab.Modifiers;
import scalac.symtab.Scope;
import scalac.symtab.Scope.SymbolIterator;
import scalac.symtab.SymSet;
import scalac.symtab.Symbol;
import scalac.symtab.SymbolTablePrinter;
import scalac.backend.Primitive;
import scalac.backend.Primitives;
import scalac.util.Name;
import scalac.util.Names;
import scalac.util.Debug;

/** A transformer for type erasure and bridge building
 *
 *  @author     Martin Odersky
 *  @version    1.0
 *
 *  What it does:
 *  (1) Map every type to its erasure.
 *  (2) If method A overrides a method B, and the erased type ETA of A is
 *      different from the erased type ETB of B seen as a member of A's class,
 *      add a bridge method with the same name as A,B, with signature ETB
 *      which calls A after casting parameters.
 */
public class Erasure extends GenTransformer implements Modifiers {

    //########################################################################
    // Private Constants

    /** The unboxed types */
    private static final Type
        UNBOXED_UNIT    = Type.unboxedType(TypeTags.UNIT),
        UNBOXED_BOOLEAN = Type.unboxedType(TypeTags.BOOLEAN),
        UNBOXED_BYTE    = Type.unboxedType(TypeTags.BYTE),
        UNBOXED_SHORT   = Type.unboxedType(TypeTags.SHORT),
        UNBOXED_CHAR    = Type.unboxedType(TypeTags.CHAR),
        UNBOXED_INT     = Type.unboxedType(TypeTags.INT),
        UNBOXED_LONG    = Type.unboxedType(TypeTags.LONG),
        UNBOXED_FLOAT   = Type.unboxedType(TypeTags.FLOAT),
        UNBOXED_DOUBLE  = Type.unboxedType(TypeTags.DOUBLE);

    //########################################################################
    // Private Fields

    /** The global definitions */
    private final Definitions definitions;

    /** The global primitives */
    private final Primitives primitives;

    /** The current unit */
    private Unit unit;

    private final boolean forMSIL;

    //########################################################################
    // Public Constructors

    /** Initializes this instance. */
    public Erasure(Global global) {
        super(global);
	this.definitions = global.definitions;
        this.primitives = global.primitives;
        this.forMSIL = global.target == global.TARGET_MSIL;
    }

    //########################################################################
    // Public Methods

    /** Transforms the given unit. */
    public void apply(Unit unit) {
	this.unit = unit;
	super.apply(unit);
    }

    /** Transforms the given tree. */
    public Tree transform(Tree tree) {
        switch (tree) {

	case ClassDef(_, _, _, _, _, Template(_, Tree[] body)):
            Symbol clasz = tree.symbol();
            TreeList members = new TreeList(transform(body));
            checkOverloadedTermsOf(clasz);
            addBridges(clasz, members);
            return gen.ClassDef(clasz, members.toArray());

	case ValDef(_, _, _, Tree rhs):
            Symbol field = tree.symbol();
	    if (rhs != Tree.Empty) rhs = transform(rhs, field.nextType());
	    return gen.ValDef(field, rhs);

	case DefDef(_, _, _, _, _, Tree rhs):
            Symbol method = tree.symbol();
            if (rhs != Tree.Empty)
                rhs = transform(rhs, method.nextType().resultType());
	    return gen.DefDef(method, rhs);

        case LabelDef(_, Ident[] params, Tree body):
            Symbol label = tree.symbol();
            body = transform(body, label.nextType().resultType());
	    return gen.LabelDef(label, Tree.symbolOf(params), body);

	case Assign(Tree lhs, Tree rhs):
	    lhs = transform(lhs);
	    rhs = transform(rhs, lhs.type);
	    return gen.Assign(tree.pos, lhs, rhs);

	case Return(Tree expr):
            Symbol method = tree.symbol();
            Type type = method.nextType().resultType();
	    return gen.Return(tree.pos, method, transform(expr, type));

        case New(Template(Tree[] base, Tree[] body)):
            assert base.length == 1 && body.length == 0: tree;
            if (tree.getType().symbol() == definitions.ARRAY_CLASS) {
                switch (base[0]) {
                case Apply(_, Tree[] args):
                    assert args.length == 1: tree;
                    Type element = getArrayElementType(tree.getType()).erasure();
                    Tree size = transform(args[0]);
                    return genNewUnboxedArray(tree.pos, element, size);
                default:
                    throw Debug.abort("illegal case", tree);
                }
            }
	    return gen.New(tree.pos, transform(base[0]));

	case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs):
            fun = transform(fun);
            vargs = transform(vargs);
            Symbol symbol = fun.symbol();
            if (symbol == definitions.ANY_AS) {
                assert targs.length == 1 && vargs.length == 0: tree;
                return coerce(getQualifier(fun), targs[0].getType().erasure());
            }
            if (symbol == definitions.ANY_IS) {
                assert targs.length == 1 && vargs.length == 0: tree;
                Type type = targs[0].type.erasure();
                if (isUnboxedSimpleType(type)) type = targs[0].type;
                return gen.mkIsInstanceOf(tree.pos, getQualifier(fun), type);
            }
            return genApply(tree.pos, fun, vargs);

	case Apply(Tree fun, Tree[] vargs):
            fun = transform(fun);
            vargs = transform(vargs);
            switch (fun) {
            case Select(Apply(Tree bfun, Tree[] bargs), _):
                Symbol bsym = bfun.symbol();
                if (primitives.getPrimitive(bsym) != Primitive.BOX) break;
                assert bargs.length == 1: fun;
                switch (primitives.getPrimitive(fun.symbol())) {
                case COERCE:
                    assert vargs.length == 0: tree;
                    Tree value = bargs[0];
                    return coerce(value, fun.type().resultType());
                case LENGTH:
                    assert vargs.length == 0: tree;
                    Tree array = bargs[0];
                    return genUnboxedArrayLength(tree.pos, array);
                case APPLY:
                    assert vargs.length == 1: tree;
                    Tree array = bargs[0];
                    Tree index = vargs[0];
                    return genUnboxedArrayGet(tree.pos, array, index);
                case UPDATE:
                    assert vargs.length == 2: tree;
                    Tree array = bargs[0];
                    Tree index = vargs[0];
                    Tree value = vargs[1];
                    return genUnboxedArraySet(tree.pos, array, index, value);
                }
            }
            return genApply(tree.pos, fun, vargs);

	case Select(Tree qualifier, _):
            Symbol symbol = tree.symbol();
            Type prefix = qualifier.type().baseType(symbol.owner()).erasure();
            assert prefix != Type.NoType: tree + " -- " + Debug.show(symbol);
	    qualifier = transform(qualifier);
	    qualifier = coerce(qualifier, prefix);

            // Might end up with "box(unbox(...))". That's needed by backend.
            if (isUnboxedType(prefix)) qualifier = box(qualifier, true);
	    return gen.Select(tree.pos, qualifier, symbol);

        case Literal(AConstant.ZERO):
	    return gen.mkNullLit(tree.pos);

        case Block(_):
	case If(_, _, _):
        case Switch(_, _, _, _):
            return transform(tree, tree.getType().fullErasure());

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

    //########################################################################
    // Private Methods - Tree transformation

    /** Transforms the given trees with given prototype. */
    private Tree[] transform(Tree[] trees, Type pt) {
        for (int i = 0; i < trees.length; i++) {
            Tree tree = transform(trees[i], pt);
            if (tree == trees[i]) continue;
            Tree[] array = new Tree[trees.length];
            for (int j = 0; j < i ; j++) array[j] = trees[j];
            array[i] = tree;
            while (++i < trees.length) array[i] = transform(trees[i], pt);
            return array;
        }
        return trees;
    }

    /** Transforms the given tree with given prototype. */
    private Tree transform(Tree tree, Type pt) {
        switch (tree) {

        case Block(Tree[] stats):
            if (stats.length == 0) return transformUnit(tree.pos, pt);
            stats = Tree.cloneArray(stats);
            for (int i = 0; i < stats.length - 1; i++)
                stats[i] = transform(stats[i]);
            stats[stats.length - 1] = transform(stats[stats.length - 1], pt);
            return gen.Block(tree.pos, stats);

	case If(Tree cond, Tree thenp, Tree elsep):
	    cond = transform(cond, UNBOXED_BOOLEAN);
	    thenp = transform(thenp, pt);
	    elsep = transform(elsep, pt);
	    return gen.If(tree.pos, cond, thenp, elsep, pt);

        case Switch(Tree test, int[] tags, Tree[] bodies, Tree otherwise):
	    test = transform(test, UNBOXED_INT);
            bodies = transform(bodies, pt);
            otherwise = transform(otherwise, pt);
            return gen.Switch(tree.pos, test, tags, bodies, otherwise, pt);

        case Return(_):
            Tree value = transform(gen.mkDefaultValue(tree.pos, pt), pt);
            return gen.mkBlock(new Tree[] {transform(tree), value});

        case LabelDef(_, _, _):
	case Assign(_, _):
        case New(_):
        case Apply(_, _):
        case Super(_, _):
        case This(_):
        case Select(_, _):
        case Ident(_):
        case Literal(_):
            return coerce(transform(tree), pt);

        default:
            throw Debug.abort("illegal case", tree);
        }
    }

    /** Transforms Unit literal with given prototype. */
    private Tree transformUnit(int pos, Type pt) {
        Tree unit = pt.isSameAs(UNBOXED_UNIT)
            ? gen.mkUnitLit(pos)
            : gen.mkApply__(gen.mkGlobalRef(pos, primitives.BOX_UVALUE));
        return coerce(unit, pt);
    }

    /** Coerces the given tree to the given type. */
    private Tree coerce(Tree tree, Type pt) {
        if (isSubType(tree.type(), pt)) {
            if (tree.type().symbol() == definitions.ARRAY_CLASS) {
                if (pt.symbol() != definitions.ARRAY_CLASS) {
                    Symbol symbol = primitives.UNBOX__ARRAY;
                    Tree unboxtree = gen.mkGlobalRef(tree.pos, symbol);
                    tree = gen.mkApply_V(unboxtree, new Tree[] {tree});
                    return coerce(tree, pt);
                }
            }
            return tree;
        }
        if (isUnboxedSimpleType(tree.type())) {
            if (isUnboxedSimpleType(pt)) return convert(tree, pt);
            Type to = pt.erasure();
            if (!isUnboxedSimpleType(to) || to.isSameAs(tree.type()))
                return box(tree);
            else
                return coerce(convert(tree, to), pt);
        } else if (isUnboxedArrayType(tree.type())) {
            if (!isUnboxedArrayType(pt)) return coerce(box(tree), pt);
        } else if (isUnboxedSimpleType(pt)) {
            Type from = tree.type().erasure();
            if (isUnboxedSimpleType(from))
                return convert(unbox(tree, from), pt);
            else
                return unbox(coerce(tree, boxUnboxedType(pt)), pt);
        } else if (isUnboxedArrayType(pt)) {
            if (tree.type.symbol() == definitions.ARRAY_CLASS)
                return unbox(tree, pt);
        } else if (pt.symbol() == definitions.ARRAY_CLASS) {
            Tree boxtree = gen.mkGlobalRef(tree.pos, primitives.BOX__ARRAY);
            return gen.mkApply_V(boxtree, new Tree[]{tree});
        }
        return gen.mkAsInstanceOf(tree, pt);
    }

    /** Boxes the given tree. */
    private Tree box(Tree tree) {
        return box(tree, false);
    }

    /** Boxes the given tree. */
    private Tree box(Tree tree, boolean force) {
        switch (tree) {
        case Apply(Tree fun, Tree[] args):
            if (primitives.getPrimitive(fun.symbol()) == Primitive.UNBOX) {
                assert args.length == 1: tree;
                if (!force) return args[0];
            }
        }
        Symbol symbol = primitives.getBoxValueSymbol(tree.getType());
	Tree boxtree = gen.mkGlobalRef(tree.pos, symbol);
        return tree.getType().equals(UNBOXED_UNIT)
            ? gen.Block(new Tree[]{tree, gen.mkApply__(boxtree)})
            : gen.mkApply_V(boxtree, new Tree[]{tree});
    }

    /** Unboxes the given tree to the given type. */
    private Tree unbox(Tree tree, Type pt) {
        switch (tree) {
        case Apply(Tree fun, Tree[] args):
            if (primitives.getPrimitive(fun.symbol()) == Primitive.BOX) {
                assert args.length == 1: tree;
                return args[0];
            }
        }
        Symbol symbol = primitives.getUnboxValueSymbol(pt);
        return gen.mkApply_V(
            gen.mkGlobalRef(tree.pos, symbol), new Tree[]{tree});
    }

    /** Converts the given tree to the given type. */
    private Tree convert(Tree tree, Type to) {
        Symbol symbol = primitives.getConvertSymbol(tree.type(), to);
        return gen.mkApply_V(
            gen.mkGlobalRef(tree.pos, symbol), new Tree[]{tree});
    }

    //########################################################################
    // Private Methods - Tree generation

    /** Generates given bridge method forwarding to given method. */
    private Tree genBridgeMethod(Symbol bridge, Symbol method) {
        Type type = bridge.nextType();
        Tree body = genApply(bridge.pos,
            gen.Select(gen.This(bridge.pos, bridge.owner()), method),
            gen.mkLocalRefs(bridge.pos, type.valueParams()));
        return gen.DefDef(bridge, coerce(body, type.resultType()));
    }

    /** Generates an application with given function and arguments. */
    private Tree genApply(int pos, Tree fun, Tree[] args) {
        switch (fun.getType()) {
        case MethodType(Symbol[] params, Type result):
            Tree[] args1 = args;
            for (int i = 0; i < args.length; i++) {
                Tree arg = args[i];
                Tree arg1 = coerce(arg, params[i].nextType());
                if (arg1 != arg && args1 == args) {
                    args1 = new Tree[args.length];
                    for (int j = 0; j < i; j++) args1[j] = args[j];
                }
                args1[i] = arg1;
            }
            return gen.mkApply_V(pos, fun, args1);
        default:
            throw Debug.abort("illegal type " + fun.getType() + " for " + fun);
        }
    }

    /**
     * Generates a new unboxed array of given size and with elements
     * of given type.
     */
    private Tree genNewUnboxedArray(int pos, Type element, Tree size) {
        if (global.target == global.TARGET_INT) {
            Tree[] targs = {gen.mkType(pos, element)};
            Tree[] vargs = {coerce(size, UNBOXED_INT)};
            Tree fun = gen.mkGlobalRef(pos, primitives.NEW_OARRAY);
            return gen.mkApplyTV(fun, targs, vargs);
        }
        switch (element) {
        case UnboxedType(int kind): return genNewUnboxedArray(pos, kind, size);
        }
        String name = primitives.getNameForClassForName(element);
        Tree[] args = { coerce(size, UNBOXED_INT), gen.mkStringLit(pos,name) };
        Tree array =
            gen.mkApply_V(gen.mkGlobalRef(pos,primitives.NEW_OARRAY), args);
        return gen.mkAsInstanceOf(array, Type.UnboxedArrayType(element));
    }

    /**
     * Generates a new unboxed array of given size and with elements
     * of given unboxed type kind.
     */
    private Tree genNewUnboxedArray(int pos, int kind, Tree size) {
        Symbol symbol = primitives.getNewArraySymbol(kind);
        Tree[] args = { coerce(size, UNBOXED_INT) };
        return gen.mkApply_V(gen.mkGlobalRef(pos, symbol), args);
    }

    /** Generates an unboxed array length operation. */
    private Tree genUnboxedArrayLength(int pos, Tree array) {
        assert isUnboxedArrayType(array.getType()): array;
        Symbol symbol = primitives.getArrayLengthSymbol(array.getType());
        Tree[] args = { array };
        return gen.mkApply_V(gen.mkGlobalRef(pos, symbol), args);
    }

    /** Generates an unboxed array get operation. */
    private Tree genUnboxedArrayGet(int pos, Tree array, Tree index) {
        assert isUnboxedArrayType(array.getType()): array;
        Symbol symbol = primitives.getArrayGetSymbol(array.getType());
        index = coerce(index, UNBOXED_INT);
        Tree[] args = { array, index };
        return gen.mkApply_V(gen.mkGlobalRef(pos, symbol), args);
    }

    /** Generates an unboxed array set operation. */
    private Tree genUnboxedArraySet(int pos, Tree array,Tree index,Tree value){
        assert isUnboxedArrayType(array.getType()): array;
        Symbol symbol = primitives.getArraySetSymbol(array.getType());
        index = coerce(index, UNBOXED_INT);
        value = coerce(value, getArrayElementType(array.getType()));
        Tree[] args = { array, index, value };
        return gen.mkApply_V(gen.mkGlobalRef(pos, symbol), args);
    }

    //########################################################################
    // Private Methods - Queries

    /** Returns the qualifier of the given tree. */
    private Tree getQualifier(Tree tree) {
        switch (tree) {
        case Select(Tree qualifier, _):
            return qualifier;
        default:
            throw Debug.abort("no qualifier for tree", tree);
        }
    }

    /** Are the given erased types in a subtyping relation? */
    private boolean isSubType(Type tp1, Type tp2) {
        global.nextPhase();
        boolean result = tp1.isSubType(tp2);
        global.prevPhase();
        return result;
    }

    /** Are the given erased types in an equality relation? */
    private boolean isSameAs(Type tp1, Type tp2) {
        global.nextPhase();
        boolean result = tp1.isSameAs(tp2);
        global.prevPhase();
        return result;
    }

    /** Is the given type an unboxed type? */
    private boolean isUnboxedType(Type type) {
	switch (type) {
	case UnboxedType(_)     : return true;
	case UnboxedArrayType(_): return true;
	default                 : return false;
	}
    }

    /** Is the given type an unboxed simple type? */
    private boolean isUnboxedSimpleType(Type type) {
	switch (type) {
	case UnboxedType(_)     : return true;
	default                 : return false;
	}
    }

    /** Is the given type an unboxed array type? */
    private boolean isUnboxedArrayType(Type type) {
	switch (type) {
	case UnboxedArrayType(_): return true;
	default                 : return false;
	}
    }

    /** Returns the boxed version of the given unboxed type. */
    private Type boxUnboxedType(Type type) {
	switch (type) {
	case UnboxedType(TypeTags.UNIT):
            return definitions.UNIT_CLASS.type();
	case UnboxedType(TypeTags.BOOLEAN):
            return definitions.BOOLEAN_CLASS.type();
	case UnboxedType(TypeTags.BYTE):
            return definitions.BYTE_CLASS.type();
	case UnboxedType(TypeTags.SHORT):
            return definitions.SHORT_CLASS.type();
	case UnboxedType(TypeTags.CHAR):
            return definitions.CHAR_CLASS.type();
	case UnboxedType(TypeTags.INT):
            return definitions.INT_CLASS.type();
	case UnboxedType(TypeTags.LONG):
            return definitions.LONG_CLASS.type();
	case UnboxedType(TypeTags.FLOAT):
            return definitions.FLOAT_CLASS.type();
	case UnboxedType(TypeTags.DOUBLE):
            return definitions.DOUBLE_CLASS.type();
	case UnboxedArrayType(Type element):
            return Type.appliedType(
                definitions.ARRAY_CLASS.type(), new Type[] {element});
	default:
            throw Debug.abort("illegal case", type);
	}
    }

    /** Returns the element type of the given array type. */
    private Type getArrayElementType(Type type) {
        switch (type) {
        case TypeRef(_, Symbol symbol, Type[] args):
            if (symbol != definitions.ARRAY_CLASS) break;
            assert args.length == 1: type;
            return args[0];
        case UnboxedArrayType(Type element):
            return element;
        }
        throw Debug.abort("non-array type", type);
    }

    //########################################################################
    // Private Methods - Overlapping signatures detection

    /**
     * Checks that overloaded terms of the given class have no
     * overlapping erased signatures.
     */
    private void checkOverloadedTermsOf(Symbol clasz) {
        for (SymbolIterator si = clasz.members().iterator(); si.hasNext(); ) {
            Symbol symbol = si.next();
            if (!symbol.isTerm()) continue;
            switch (symbol.info()) {
            case OverloadedType(Symbol[] symbols, _):
                Type[] types = new Type[symbols.length];
                for (int i = 0; i < symbols.length; i++) {
                    types[i] = symbols[i].nextType();
                    for (int j = 0; j < i; j++) {
                        if (!types[i].isSameAs(types[j])) continue;
                        errorOverlappingSignatures(symbols[j], symbols[i]);
                        break;
                    }
                }
            }
        }
    }

    /** Reports an overlapping signature error for given symbols. */
    private void errorOverlappingSignatures(Symbol symbol1, Symbol symbol2) {
        SymbolTablePrinter printer = new SymbolTablePrinter(" ");
        printer.print("overlapping overloaded alternatives;").space();
        printer.print("the two following alternatives of").space();
        printer.printSymbol(symbol1).space();
        printer.print("have the same erasure:").space();
        printer.printType(symbol1.nextType());
        Phase phase = global.currentPhase;
        global.currentPhase = global.PHASE.ANALYZER.phase();
        printer.indent();
        printer.line().print("alternative 1:").space().printSignature(symbol1);
        printer.line().print("alternative 2:").space().printSignature(symbol2);
        printer.undent();
        global.currentPhase = phase;
        unit.error(symbol2.pos, printer.toString());
    }

    //########################################################################
    //########################################################################
    //########################################################################
    //########################################################################

//////////////////////////////////////////////////////////////////////////////////
// Bridge Building
/////////////////////////////////////////////////////////////////////////////////

    private TreeList bridges;
    private HashMap bridgeSyms;

    /** Add bridge which Java-overrides `sym1' and which forwards to `sym'
     */
    public void addBridge(Symbol owner, Symbol sym, Symbol sym1) {
	Type bridgeType = sym1.nextType();

	// create bridge symbol and add to bridgeSyms(sym)
	// or return if bridge with required type already exists for sym.
	SymSet bridgesOfSym = (SymSet) bridgeSyms.get(sym);
	if (bridgesOfSym == null) bridgesOfSym = SymSet.EMPTY;
	Symbol[] brs = bridgesOfSym.toArray();
	for (int i = 0; i < brs.length; i++) {
	    if (isSameAs(brs[i].type(), bridgeType)) return;
	}
	Symbol bridgeSym = sym.cloneSymbol();
	bridgeSym.flags |= (SYNTHETIC | BRIDGE);
	bridgeSym.flags &= ~(JAVA | DEFERRED);
	bridgesOfSym = bridgesOfSym.incl(bridgeSym);
	bridgeSyms.put(sym, bridgesOfSym);
        bridgeSym.setOwner(owner);

	// check that there is no overloaded symbol with same erasure as bridge
	// todo: why only check for overloaded?
	Symbol overSym = owner.members().lookup(sym.name);
	switch (overSym.nextType()) {
	case OverloadedType(Symbol[] alts, Type[] alttypes):
	    for (int i = 0; i < alts.length; i++) {
		if (sym != alts[i] && isSameAs(bridgeType, alttypes[i])) {
		    unit.error(sym.pos, "overlapping overloaded alternatives; " +
			       "overridden " + sym1 + sym1.locationString() +
			       " has same erasure as " + alts[i] +
			       alttypes[i] + alts[i].locationString());
		}
	    }
	}

	switch (bridgeType) {
	case MethodType(Symbol[] params, Type restp):
	    // assign to bridge symbol its bridge type
	    // where owner of all parameters is bridge symbol itself.
	    Symbol[] params1 = new Symbol[params.length];
	    for (int i = 0; i < params.length; i++) {
		params1[i] = params[i].cloneSymbol(bridgeSym);
	    }
	    bridgeSym.setType(Type.MethodType(params1, restp));

            bridges.append(genBridgeMethod(bridgeSym, sym));
        }

    }

    /** Add an "empty bridge" (abstract method declaration) to satisfy
     *  CLR's requirement that classes should provide declaration
     *  for all methods of the interfaces they implement
     */
    public void addEmptyBridge(Symbol owner, Symbol method) {
	Type bridgeType = method.nextType();
	Symbol bridgeSym = method.cloneSymbol(owner);
	bridgeSym.flags = bridgeSym.flags & ~JAVA | SYNTHETIC | DEFERRED;
	//bridgeSym.setOwner(owner);
	switch (bridgeType) {
	case MethodType(Symbol[] params, Type restp):
	    // assign to bridge symbol its bridge type
	    // where owner of all parameters is bridge symbol itself.
	    Symbol[] params1 = new Symbol[params.length];
	    for (int i = 0; i < params.length; i++) {
		params1[i] = params[i].cloneSymbol(bridgeSym);
	    }
	    bridgeSym.setType(Type.MethodType(params1, restp));
	    Tree bridge = gen.DefDef(bridgeSym, Tree.Empty);
            bridges.append(bridge);
	}
    }

    private final Map interfaces/*<Symbol,Set<Symbol>>*/ = new HashMap();

    private Set getInterfacesOf(Symbol clasz) {
        assert clasz.isClass(): Debug.show(clasz);
        Set set = (Set)interfaces.get(clasz);
        if (set == null) {
            set = new HashSet();
            interfaces.put(clasz, set);
            Type parents[] = clasz.parents();
            for (int i = 0; i < parents.length; i++)
                set.addAll(getInterfacesOf(parents[i].symbol()));
            if (clasz.isInterface()) set.add(clasz);
        }
        return set;
    }

    private void addInterfaceBridges_(Symbol clasz) {
        assert clasz.isClass() && !clasz.isInterface(): Debug.show(clasz);
        assert clasz.parents().length > 0: Debug.show(clasz)+": "+clasz.info();
        Symbol svper = clasz.parents()[0].symbol();
        assert svper.isClass() && !svper.isInterface(): Debug.show(clasz);
        Set interfaces = new HashSet(getInterfacesOf(clasz));
        interfaces.removeAll(getInterfacesOf(svper));
        for (Iterator i = interfaces.iterator(); i.hasNext(); ) {
            Symbol inter = (Symbol)i.next();
            addInterfaceBridgesAux(clasz, inter.members());
        }
    }

    private void addInterfaceBridgesAux(Symbol owner, Scope symbols) {
        for (Scope.SymbolIterator i = symbols.iterator(true); i.hasNext();) {
            Symbol member = i.next();
            if (!member.isTerm() || !member.isDeferred()) continue;
            addInterfaceBridges(owner, member);
        }
    }


    private Symbol getOverriddenMethod(Symbol method) {
        Type[] parents = method.owner().parents();
        if (parents.length == 0) return Symbol.NONE;
        return method.overriddenSymbol(parents[0]);
    }

    public void addBridgeMethodsTo(Symbol method) {
        assert method.owner().isClass() && !method.owner().isInterface();
        Symbol overridden = getOverriddenMethod(method);
        if (overridden != Symbol.NONE && !isSameAs(overridden.nextType(), method.nextType()))
            addBridge(method.owner(), method, overridden);
    }

    public void addInterfaceBridges(Symbol owner, Symbol method) {
	assert owner.isClass() && !owner.isInterface(): Debug.show(owner);
        Symbol overriding = method.overridingSymbol(owner.thisType());
        if (overriding == method) {
            Symbol overridden = method.overriddenSymbol(owner.thisType().parents()[0], owner);
            if (overridden != Symbol.NONE && !isSameAs(overridden.nextType(), method.nextType()))
                addBridge(owner, method, overridden);
	    if (forMSIL && (overridden == Symbol.NONE
			    || (overridden != Symbol.NONE && overridden.owner() != owner)))
		    addEmptyBridge(owner, method);
        } else if (overriding != Symbol.NONE && !isSameAs(overriding.nextType(), method.nextType()))
            addBridge(owner, overriding, method);
    }

    private void addBridges(Symbol clasz, TreeList members) {
        TreeList savedBridges = bridges;
        HashMap savedBridgeSyms = bridgeSyms;
        bridges = new TreeList();
        bridgeSyms = new HashMap();

        int length = members.length();
        if (!clasz.isInterface()) {
            for (int i = 0; i < length; i++) {
                switch (members.get(i)) {
                case DefDef(_, _, _, _, _, Tree rhs):
                    addBridgeMethodsTo(members.get(i).symbol());
                }
            }
            addInterfaceBridges_(clasz);
        }

        members.append(bridges);
        if (bridges.length() > 0) {
            Type info = clasz.nextInfo();
            switch (info) {
            case CompoundType(Type[] parts, Scope members_):
                members_ = members_.cloneScope();
                for (int i = 0; i < bridges.length(); i++) {
                    Tree bridge = (Tree)bridges.get(i);
                    members_.enterOrOverload(bridge.symbol());
                }
                clasz.updateInfo(Type.compoundType(parts, members_, info.symbol()));
                break;
            default:
                throw Debug.abort("class = " + Debug.show(clasz) + ", " +
                    "info = " + Debug.show(info));
            }
        }
        bridgeSyms = savedBridgeSyms;
        bridges = savedBridges;
    }


}