From c00e8c765a48fd5f568c1fbabfd88ab98ec6815c Mon Sep 17 00:00:00 2001 From: paltherr Date: Fri, 21 Feb 2003 10:31:12 +0000 Subject: - Added Erasure and ErasurePhase --- config/list/compiler.lst | 4 +- sources/scalac/transformer/Erasure.java | 586 +++++++++++++++++++++++++++ sources/scalac/transformer/ErasurePhase.java | 90 ++++ 3 files changed, 678 insertions(+), 2 deletions(-) create mode 100644 sources/scalac/transformer/Erasure.java create mode 100644 sources/scalac/transformer/ErasurePhase.java diff --git a/config/list/compiler.lst b/config/list/compiler.lst index a20dd6a5aa..729d8bcc3d 100644 --- a/config/list/compiler.lst +++ b/config/list/compiler.lst @@ -72,8 +72,8 @@ transformer/AddAccessors.java transformer/AddAccessorsPhase.java transformer/AddInterfaces.java transformer/AddInterfacesPhase.java -# transformer/Erasure.java -# transformer/ErasurePhase.java +transformer/Erasure.java +transformer/ErasurePhase.java transformer/ExplicitOuterClasses.java transformer/ExplicitOuterClassesPhase.java transformer/ExpandMixins.java diff --git a/sources/scalac/transformer/Erasure.java b/sources/scalac/transformer/Erasure.java new file mode 100644 index 0000000000..fee6b4dfbc --- /dev/null +++ b/sources/scalac/transformer/Erasure.java @@ -0,0 +1,586 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ 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.HashMap; + +import scalac.Global; +import scalac.PhaseDescriptor; +import scalac.Unit; +import scalac.ast.Tree; +import scalac.ast.Tree.Template; +import scalac.ast.Tree.TypeDef; +import scalac.ast.Tree.ValDef; +import scalac.ast.TreeList; +import scalac.ast.Transformer; +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.SymSet; +import scalac.symtab.Symbol; +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 Transformer implements Modifiers { + + private final Global global; + private final Definitions definitions; + private final Primitives primitives; + private final boolean noTyped; + + private Unit unit; + + public Erasure(Global global, PhaseDescriptor descr) { + super(global, descr); + this.global = global; + this.definitions = global.definitions; + this.primitives = global.primitives; + this.noTyped = global.target != global.TARGET_JAVA; + } + + public void apply(Unit unit) { + this.unit = unit; + unit.body = transform(unit.body); + } + +////////////////////////////////////////////////////////////////////////////////// +// Box/Unbox and Coercions +///////////////////////////////////////////////////////////////////////////////// + + boolean isUnboxed(Type type) { + switch (type) { + case UnboxedType(_): case UnboxedArrayType(_): return true; + default: return false; + } + } + + boolean isUnboxedArray(Type type) { + switch (type) { + case UnboxedArrayType(_): return true; + default: return false; + } + } + + boolean isBoxed(Type type) { + return type.unbox() != type || type.symbol().fullName() == Names.scala_Array; + } + + Type boxedType(Type tp) { + switch (tp) { + case UnboxedType(int kind): + return definitions.getType(Type.boxedFullName(kind)); + case UnboxedArrayType(Type elemtp): + return definitions.arrayType(boxedType(elemtp)); + default: + return tp; + } + } + + Symbol boxSym(Type unboxedtp) { + return primitives.getBoxValueSymbol(unboxedtp); + } + + /** Emit `scala.RunTime.box(tree)' or + * `{ tree ; scala.RunTime.box() }' if type of `tree' is `void'. + */ + Tree box(Tree tree) { + Tree boxtree = gen.mkRef(tree.pos, primitives.RUNTIME_TYPE, boxSym(tree.type)); + switch (tree.type) { + case UnboxedType(int kind): + if (kind == TypeTags.UNIT) + return gen.Block( + tree.pos, new Tree[]{tree, gen.Apply(boxtree, new Tree[0])}); + } + return gen.Apply(boxtree, new Tree[]{tree}); + } + + /** The symbol of the unbox method corresponding to unboxed type`unboxedtp' + */ + Symbol unboxSym(Type unboxedtp) { + return primitives.getUnboxValueSymbol(unboxedtp); + } + + /** Emit tree.asType() or tree.asTypeArray(), where pt = Type or pt = Type[]. + */ + Tree unbox(Tree tree, Type pt) { + Tree sel = gen.Select(tree, unboxSym(pt)); + return gen.Apply(sel, new Tree[0]); + } + + /** Generate a select from an unboxed type. + */ + public Tree unboxedSelect(Tree qual, Symbol sym) { + return make.Select(qual.pos, qual, sym.name) + .setSymbol(sym) + // !!! .setType(Prefix.TypePrefix(boxedType(qual.type)).memberType(sym).erasure()); + .setType(Type.singleType(boxedType(qual.type),sym).erasure()); + } + + /** Subclass relation for class types; empty for other types. + */ + boolean isSubClass(Type tp1, Type tp2) { + Symbol sym1 = tp1.symbol(); + Symbol sym2 = tp2.symbol(); + return sym1 != null && sym2 != null && sym1.isSubClass(sym2); + } + + /** Subtyping relation on erased types. + */ + boolean isSubType(Type tp1, Type tp2) { + if (tp1.isSameAs(tp2)) return true; + switch (tp2) { + case UnboxedType(_): + return tp1.isSubType(tp2); + case UnboxedArrayType(Type elemtp2): + switch (tp1) { + case UnboxedArrayType(Type elemtp1): + return !(elemtp1 instanceof Type.UnboxedType) && + isSubType(elemtp1, elemtp2); + } + } + return isSubClass(tp1, tp2); + } + + Tree coerce(Tree tree, Type pt) { + return isSubType(tree.type, pt) ? tree : cast(tree, pt); + } + + Tree cast(Tree tree, Type pt) { + if (global.debug) global.log("cast " + tree + ":" + tree.type + " to " + pt);//debug + if (tree.type.isSameAs(pt)) { + return tree; + } else if (isSubType(tree.type, pt)) { + return noTyped ? tree : gen.Typed(tree, pt); + } else if (isUnboxed(tree.type) && !isUnboxed(pt)) { + return cast(box(tree), pt); + } else if ((isUnboxedArray(tree.type) + || (tree.type.symbol() == definitions.ANY_CLASS)) + && isUnboxedArray(pt)) { + return + make.Apply(tree.pos, + make.TypeApply(tree.pos, + unboxedSelect(tree, definitions.AS), + new Tree[]{gen.mkType(tree.pos, pt)}) + .setType(new Type.MethodType(Symbol.EMPTY_ARRAY, pt)), + new Tree[0]) + .setType(pt); + } else if (!isUnboxed(tree.type) && isUnboxed(pt)) { + if (isBoxed(tree.type)) { + return coerce(unbox(tree, pt), pt); + } else { + Type bt = boxedType(pt); + while (isBoxed(bt.parents()[0])) { + bt = bt.parents()[0]; + } + return cast(coerce(tree, bt), pt); + } + } else if (isUnboxed(tree.type) && isUnboxed(pt)) { + return gen.Apply( + unboxedSelect(box(tree), unboxSym(pt)), + new Tree[0]); + } else if (!isUnboxed(tree.type) && !isUnboxed(pt) || + isUnboxedArray(tree.type) && isUnboxedArray(pt)) { + return + gen.Apply( + gen.TypeApply( + gen.Select(tree, definitions.AS), + new Tree[]{gen.mkType(tree.pos, pt)}), + new Tree[0]); + } else { + throw Debug.abort("cannot cast " + tree.type + " to " + pt); + } + } + +////////////////////////////////////////////////////////////////////////////////// +// Bridge Building +///////////////////////////////////////////////////////////////////////////////// + + private TreeList bridges; + private HashMap bridgeSyms; + + /** Add bridge which Java-overrides `sym1' and which forwards to `sym' + */ + public void addBridge(Symbol sym, Symbol sym1) { + Type bridgeType = sym1.type().erasure(); + + // 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 (brs[i].type().isSameAs(bridgeType)) return; + } + Symbol bridgeSym = sym.cloneSymbol(); + bridgeSym.flags |= (SYNTHETIC | BRIDGE); + bridgeSym.flags &= ~JAVA; + bridgesOfSym = bridgesOfSym.incl(bridgeSym); + bridgeSyms.put(sym, bridgesOfSym); + + // check that there is no overloaded symbol with same erasure as bridge + Symbol overSym = sym.owner().members().lookup(sym.name); + switch (overSym.type()) { + case OverloadedType(Symbol[] alts, Type[] alttypes): + for (int i = 0; i < alts.length; i++) { + if (sym != alts[i] && bridgeType.isSameAs(alttypes[i].erasure())) { + 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(); + params1[i].setOwner(bridgeSym); + } + bridgeSym.setType(Type.MethodType(params1, restp)); + + // create bridge definition + Type symtype = sym.type().erasure(); + switch (symtype) { + case MethodType(Symbol[] symparams, Type symrestp): + assert params1.length == symparams.length; + Tree[] args = new Tree[params1.length]; + for (int i = 0; i < args.length; i++) { + args[i] = cast(gen.Ident(params1[i]), symparams[i].type()); + } + Tree fwd = make.Apply(sym.pos, gen.Ident(sym).setType(symtype), args) + .setType(symrestp); + bridges.append(gen.DefDef(bridgeSym, coerce(fwd, restp))); + return; + } + } + throw Debug.abort("bad bridge types " + bridgeType + "," + sym.type().erasure()); + } + + public void addBridges(Symbol sym) { + Symbol c = sym.owner(); + if (c.isClass() && !c.isInterface()) { + Type[] basetypes = c.parents(); + + //System.out.println("trying " + c + " <= " + ArrayApply.toString(c.basetypes()));//DEBUG + + for (int i = 0; i < basetypes.length; i++) { + Symbol sym1 = sym.overriddenSymbol(basetypes[i]); + + //if (sym1.kind != NONE) System.out.println("overridden: " + sym1 + sym1.locationString() + " by " + sym + sym.locationString());//DEBUG + + if (sym1.kind != Kinds.NONE && + !sym1.type().erasure().isSameAs(sym.type().erasure())) { + + //System.out.println("add bridge: " + sym1 + sym1.locationString() + " by " + sym + sym.locationString());//DEBUG + + addBridge(sym, sym1); + } + } + } + } + +////////////////////////////////////////////////////////////////////////////////// +// Transformer +///////////////////////////////////////////////////////////////////////////////// + + /** Contract: every node needs to be transformed so that it's type is the + * erasure of the node's original type. The only exception are functions; + * these are mapped to the erasure of the function symbol's type. + */ + Symbol currentClass = null; + public Tree transform(Tree tree, boolean eraseFully) { + assert tree.type != null : tree; + Type owntype = eraseFully ? tree.type.fullErasure() : tree.type.erasure(); + switch (tree) { + case ClassDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl): + Symbol oldCurrentClass = currentClass; + currentClass = tree.symbol(); + Tree newTree = + copy.ClassDef(tree, mods, name, new TypeDef[0], + transform(vparams), tpe, transform(impl, tree.symbol())) + .setType(owntype); + currentClass = oldCurrentClass; + return newTree; + + case DefDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Tree rhs): + addBridges(tree.symbol()); + Tree tpe1 = gen.mkType(tpe.pos, tpe.type.fullErasure()); + Tree rhs1 = (rhs == Tree.Empty) ? rhs : transform(rhs, tpe1.type); + return copy.DefDef( + tree, mods, name, new TypeDef[0], transform(vparams), tpe1, rhs1) + .setType(owntype); + + case ValDef(int mods, Name name, Tree tpe, Tree rhs): + Tree tpe1 = transform(tpe); + Tree rhs1 = (rhs == Tree.Empty) ? rhs : transform(rhs, tpe1.type); + return copy.ValDef( + tree, mods, name, tpe1, rhs1) + .setType(owntype); + + case TypeDef(_, _, _, _): + // eliminate + return Tree.Empty; + + case Block(Tree[] stats): + Tree[] newStats = new Tree[stats.length]; + for (int i = 0; i < stats.length; ++i) + newStats[i] = transform(stats[i], true); + return copy.Block(tree, newStats).setType(owntype.fullErasure()); + + case Assign(Tree lhs, Tree rhs): + Tree lhs1 = transformLhs(lhs); + Tree rhs1 = transform(rhs, lhs1.type); + return copy.Assign(tree, lhs1, rhs1).setType(owntype.fullErasure()); + + case If(Tree cond, Tree thenp, Tree elsep): + Tree cond1 = transform(cond, Type.unboxedType(TypeTags.BOOLEAN)); + Tree thenp1 = transform(thenp, owntype); + Tree elsep1 = (elsep == Tree.Empty) ? elsep : transform(elsep, owntype); + return copy.If(tree, cond1, thenp1, elsep1).setType(owntype); + + case New(Template templ): + if (tree.type.symbol() == definitions.UNIT_CLASS) + // !!! return Tree.Literal(UNIT, null).setType(owntype); + throw Debug.abort("found unit literal"); + switch (owntype) { + case UnboxedArrayType(Type elemtp): + Tree apply = transform(templ.parents[0]); + switch (apply) { + case Apply(_, Tree[] args): + assert args.length == 1; + switch (elemtp) { + case UnboxedType(int kind): + return genNewArray(tree.pos,args[0],kind); + default: + return genNewArray(tree.pos,args[0],elemtp); + } + default: + throw Debug.abort("illegal case", apply); + } + } + return super.transform(tree).setType(owntype); + + case Typed(Tree expr, Tree tpe): + // coerce expr to tpe + Tree tpe1 = transform(tpe); + Tree expr1 = transform(expr, tpe1.type); + return noTyped ? expr1 : copy.Typed(tree, expr1, tpe1).setType(owntype); + + case TypeApply(Tree fun, Tree[] args): + Symbol sym = fun.symbol(); + if (sym == definitions.AS || sym == definitions.IS) { + Type tp = args[0].type.erasure(); + if (isUnboxed(tp)) { + Tree qual1 = transform(getQualifier(currentClass, fun)); + if (isUnboxed(qual1.type)) qual1 = box(qual1); + Symbol primSym = (sym == definitions.AS) + ? primitives.getUnboxValueSymbol(tp) + : primitives.getInstanceTestSymbol(tp); + return gen.Select(qual1, primSym); + } else + return copy.TypeApply(tree, transform(fun), transform(args)) + .setType(owntype); + } else + return transform(fun); + + case Apply(Tree fun, Tree[] args): + switch (fun) { + case Select(Tree array, _): + if (isUnboxedArray(array.type().erasure())) { + switch (primitives.getPrimitive(fun.symbol())) { + case APPLY: return transformApply(tree); + case UPDATE: return transformUpdate(tree); + } + } + } + Tree fun1 = transform(fun); + if (fun1.symbol() == definitions.NULL) return fun1.setType(owntype); + if (global.debug) global.log("fn: " + fun1.symbol() + ":" + fun1.type);//debug + switch (fun1.type) { + case MethodType(Symbol[] params, Type restpe): + Tree[] args1 = args; + for (int i = 0; i < args.length; i++) { + Tree arg = args[i]; + Type pt1 = params[i].type().erasure(); + Tree arg1 = cast(transform(arg, pt1), pt1); + if (arg1 != arg && args1 == args) { + args1 = new Tree[args.length]; + System.arraycopy(args, 0, args1, 0, i); + } + args1[i] = arg1; + } + return coerce(copy.Apply(tree, fun1, args1).setType(restpe), owntype); + default: + global.debugPrinter.print(fun1); + throw Debug.abort("bad method type: " + fun1.type + " " + fun1.symbol()); + } + + case Select(_, _): + case Ident(_): + Tree tree1 = transformLhs(tree); + //global.log("id: " + tree1+": "+tree1.type+" -> "+owntype);//DEBUG + return (tree1.type instanceof Type.MethodType) ? tree1 + : coerce(tree1, owntype); + + case AppliedType(_, _): + return gen.mkType(tree.pos, owntype); + + default: + return super.transform(tree).setType(owntype); + } + } + + public Tree transform(Tree tree) { + return transform(tree, false); + } + + public Template transform(Template templ, Symbol clazz) { + TreeList savedBridges = bridges; + HashMap savedBridgeSyms = bridgeSyms; + bridges = new TreeList(); + bridgeSyms = new HashMap(); + Tree[] bases1 = transform(templ.parents); + TreeList body1 = new TreeList(transform(templ.body)); + body1.append(bridges); + if (bridges.length() > 0) { + switch (clazz.nextInfo()) { + case CompoundType(Type[] basetypes, Scope members): + members = new Scope(members); + for (int i = 0; i < bridges.length(); i++) { + Tree bridge = (Tree)bridges.get(i); + members.enterOrOverload(bridge.symbol()); + } + clazz.updateInfo(Type.CompoundType(basetypes, members)); + break; + default: + throw Debug.abort("class = " + Debug.show(clazz) + ", " + + "info = " + Debug.show(clazz.info())); + } + } + bridges = savedBridges; + bridgeSyms = savedBridgeSyms; + return (Template) copy.Template(templ, bases1, body1.toArray()) + .setType(templ.type.erasure()); + } + + /** Transform without keeping the previous transform's contract. + */ + Tree transformLhs(Tree tree) { + Tree tree1; + switch (tree) { + case Ident(_): + tree1 = tree; + break; + case Select(Tree qual, Name name): + Tree qual1 = transform(qual); + if (isUnboxed(qual1.type)) + if (!isUnboxedArray(qual1.type) || tree.symbol() == definitions.ARRAY_CLASS) + qual1 = box(qual1); + tree1 = copy.Select(tree, qual1, name); + break; + default: + throw Debug.abort("illegal case", tree); + } + if (global.debug) global.log("id: " + tree1.symbol() + ":" + tree1.symbol().type().erasure());//debug + return tree1.setType(tree1.symbol().type().erasure()); + } + + /** Transform with prototype + */ + Tree transform(Tree expr, Type pt) { + return coerce(transform(expr), pt); + } + + /** Transform an array apply */ + Tree transformApply(Tree tree) { + switch (tree) { + case Apply(Select(Tree array, _), Tree[] args): + assert args.length == 1 : Debug.show(args); + Type finalType = tree.type().erasure(); + array = transform(array); + Symbol symbol = primitives.getArrayGetSymbol(array.type()); + Tree method = gen.mkRef(tree.pos,primitives.RUNTIME_TYPE,symbol); + args = new Tree[] { array, transform(args[0]) }; + return coerce(gen.Apply(tree.pos, method, args), finalType); + default: + throw Debug.abort("illegal case", tree); + } + } + + /** Transform an array update */ + Tree transformUpdate(Tree tree) { + switch (tree) { + case Apply(Select(Tree array, _), Tree[] args): + assert args.length == 2 : Debug.show(args); + array = transform(array); + Symbol symbol = primitives.getArraySetSymbol(array.type()); + Tree method = gen.mkRef(tree.pos,primitives.RUNTIME_TYPE,symbol); + args = new Tree[] { array, transform(args[0]),transform(args[1]) }; + return gen.Apply(tree.pos, method, args); + default: + throw Debug.abort("illegal case", tree); + } + } + + private Tree getQualifier(Symbol currentClass, Tree tree) { + switch (tree) { + case Select(Tree qual, _): + return qual; + case Ident(_): + assert currentClass != null; + if (currentClass.isSubClass(tree.symbol().owner())) + return gen.This(tree.pos, currentClass); + else + throw Debug.abort("no qualifier for tree", tree); + default: + throw Debug.abort("no qualifier for tree", tree); + } + } + + private Tree genNewArray(int pos, Tree size, Type elemtp) { + Tree classname = make.Literal(pos, + primitives.getNameForClassForName(elemtp)) + .setType(definitions.JAVA_STRING_TYPE); + Tree array = gen.Apply(pos, + gen.mkRef(pos, primitives.RUNTIME_TYPE, primitives.NEW_OARRAY), + new Tree[] {size, classname}); + Tree cast = gen.TypeApply(pos, gen.Select(pos, array, definitions.AS), + new Tree[] {gen.mkType(pos, Type.UnboxedArrayType(elemtp))}); + return gen.Apply(cast, new Tree[0]); + } + + private Tree genNewArray(int pos, Tree size, int kind) { + return gen.Apply(pos, + gen.mkRef(pos, + primitives.RUNTIME_TYPE, primitives.getNewArraySymbol(kind)), + new Tree[] {size}); + } +} diff --git a/sources/scalac/transformer/ErasurePhase.java b/sources/scalac/transformer/ErasurePhase.java new file mode 100644 index 0000000000..ed1ab1224e --- /dev/null +++ b/sources/scalac/transformer/ErasurePhase.java @@ -0,0 +1,90 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $OldId: ErasurePhase.java,v 1.13 2002/11/14 15:58:22 schinz Exp $ +// $Id$ + +package scalac.transformer; + +//import scala.compiler.parser.Kinds; +//import scala.compiler.typechecker.*; + +import scalac.Global; +import scalac.Phase; +import scalac.PhaseDescriptor; +import scalac.checkers.Checker; +import scalac.checkers.CheckOwners; +import scalac.checkers.CheckSymbols; +import scalac.checkers.CheckTypes; +import scalac.symtab.Definitions; +import scalac.symtab.Symbol; +import scalac.symtab.Type; +import scalac.util.Name; +import scalac.util.Debug; + +public class ErasurePhase extends PhaseDescriptor { + + public Definitions definitions; + + public String name () { + return "erasure"; + } + + public String description () { + return "type eraser"; + } + + public String taskDescription() { + return "erased types"; + } + + public Phase createPhase(Global global) { + this.definitions = global.definitions; + return new Erasure(global, this); + } + + private static final Name as_N = Name.fromString("as"); + private static final Name is_N = Name.fromString("is"); + private static final Name box_N = Name.fromString("box"); + private static final Name scala_runtime_RunTime_N = + Name.fromString("scala.runtime.RunTime"); + + private Type eraseParams(Type tp) { + switch (tp) { + case PolyType(_, Type result): + return eraseParams(result); + case MethodType(Symbol[] params, Type result): + Symbol[] params1 = Type.erasureMap.map(params); + if (params1 == params) return tp; + else return Type.MethodType(params1, result); + default: + return tp; + } + } + + public Type transformInfo(Symbol sym, Type tp) { + if ((sym.name == is_N || sym.name == as_N) && + sym.owner() == definitions.ANY_CLASS) + return tp; + else if (sym.name == box_N && + sym.owner().fullName() == scala_runtime_RunTime_N) + return eraseParams(tp); + else if (sym.isClass()) + return Type.erasureMap.map(tp); + else if (sym.isType()) + return tp; + else + return tp.erasure(); + } + + public Checker[] postCheckers(Global global) { + return new Checker[] { + new CheckSymbols(global), + new CheckTypes(global), + new CheckOwners(global) + }; + } +} -- cgit v1.2.3