From aae91eddee1a90dc5312ce156b772f090001721f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 28 Aug 2014 22:35:09 +0200 Subject: Fixes to erasure to make -Ycheck:all work. Main change: Introduce JavaArrayType as a new type constructor for Java erased array. Translate all methods of Array class during erasure to primitive operations on arrays. Some other small fixes for more localized problems. --- src/dotty/tools/dotc/TypeErasure.scala | 113 +++++++++++++-------- src/dotty/tools/dotc/core/Definitions.scala | 1 - src/dotty/tools/dotc/core/NameOps.scala | 8 ++ src/dotty/tools/dotc/core/StdNames.scala | 10 ++ src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- src/dotty/tools/dotc/core/TypeComparer.scala | 8 ++ src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 + src/dotty/tools/dotc/transform/Erasure.scala | 72 +++++++------ .../tools/dotc/transform/TypeTestsCasts.scala | 2 +- src/dotty/tools/dotc/typer/TypeAssigner.scala | 19 +++- 10 files changed, 151 insertions(+), 86 deletions(-) (limited to 'src/dotty/tools') diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index c8c54ed03..63b4f396b 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -8,20 +8,17 @@ import util.DotClass * * TypeRef(NoPrefix, denot is ClassDenotation) * TermRef(NoPrefix, denot is SymDenotation) - * ThisType - * SuperType - * PolyParam, only for array types and isInstanceOf, asInstanceOf - * RefinedType, parent* is Array class - * TypeBounds, only for array types + * JavaArrayType * AnnotatedType * MethodType ----+-- JavaMethodType - * PolyType, only for array types and isInstanceOf, asInstanceOf - * RefinedThis * ClassInfo * NoType * NoPrefix * WildcardType * ErrorType + * + * only for isInstanceOf, asInstanceOf: PolyType, PolyParam, TypeBounds + * */ object TypeErasure { @@ -74,18 +71,19 @@ object TypeErasure { /** The symbol's erased info. This is the type's erasure, except for the following symbols: * * - For $asInstanceOf : [T]T - * - For $isInstanceOf : [T]scala#Boolean - * - For Array[T]. : [T]{scala#Int)Array[T] - * - For type members of Array : The original info - * - For all other abstract types: = ? + * - For $isInstanceOf : [T]Boolean + * - For all abstract types : = ? * - For all other symbols : the semi-erasure of their types, with * isJava, isConstructor set according to symbol. */ def transformInfo(sym: Symbol, tp: Type)(implicit ctx: Context): Type = { val erase = erasureFn(sym is JavaDefined, isSemi = true, sym.isConstructor, wildcardOK = false) - if ((sym eq defn.Any_asInstanceOf) || - (sym eq defn.Any_isInstanceOf) || - (sym.owner eq defn.ArrayClass) && (sym.isType || sym.isConstructor)) sym.info + + def eraseParamBounds(tp: PolyType): Type = + tp.derivedPolyType( + tp.paramNames, tp.paramNames map (Function.const(TypeBounds.upper(defn.ObjectType))), tp.resultType) + + if ((sym eq defn.Any_asInstanceOf) || (sym eq defn.Any_isInstanceOf)) eraseParamBounds(sym.info.asInstanceOf[PolyType]) else if (sym.isAbstractType) TypeAlias(WildcardType) else erase(tp) } @@ -95,30 +93,56 @@ object TypeErasure { tp.classSymbol.isPrimitiveValueClass || (tp.typeSymbol is JavaDefined)) - def erasedLub(tp1: Type, tp2: Type)(implicit ctx: Context): Type = { - val cls2 = tp2.classSymbol - def loop(bcs: List[ClassSymbol], bestSoFar: ClassSymbol): ClassSymbol = bcs match { - case bc :: bcs1 => - if (cls2.derivesFrom(bc)) - if (!bc.is(Trait)) bc - else loop(bcs1, if (bestSoFar.derivesFrom(bc)) bestSoFar else bc) - else - loop(bcs1, bestSoFar) - case nil => - bestSoFar - } - loop(tp1.baseClasses, defn.ObjectClass).typeRef + /** The erased least upper bound is computed as follows + * - if both argument are arrays, an array of the lub of the element types + * - if one argument is an array, Object + * - otherwise a common superclass or trait S of the argument classes, with the + * following two properties: + * S is minimal: no other common superclass or trait derives from S] + * S is last : in the linearization of the first argument type `tp1` + * there are no minimal common superclasses or traits that + * come after S. + * (the reason to pick last is that we prefer classes over traits that way). + */ + def erasedLub(tp1: Type, tp2: Type)(implicit ctx: Context): Type = tp1 match { + case JavaArrayType(elem1) => + tp2 match { + case JavaArrayType(elem2) => JavaArrayType(erasedLub(elem1, elem2)) + case _ => defn.ObjectType + } + case _ => + tp2 match { + case JavaArrayType(_) => defn.ObjectType + case _ => + val cls2 = tp2.classSymbol + def loop(bcs: List[ClassSymbol], bestSoFar: ClassSymbol): ClassSymbol = bcs match { + case bc :: bcs1 => + if (cls2.derivesFrom(bc)) + if (!bc.is(Trait)) bc + else loop(bcs1, if (bestSoFar.derivesFrom(bc)) bestSoFar else bc) + else + loop(bcs1, bestSoFar) + case nil => + bestSoFar + } + loop(tp1.baseClasses, defn.ObjectClass).typeRef + } } + /** The erased greatest lower bound picks one of the two argument types. It prefers, in this order: + * - arrays over non-arrays + * - subtypes over supertypes, unless isJava is set + * - real classes over traits + */ def erasedGlb(tp1: Type, tp2: Type, isJava: Boolean)(implicit ctx: Context): Type = tp1 match { - case defn.ArrayType(elem1) => + case JavaArrayType(elem1) => tp2 match { - case defn.ArrayType(elem2) => defn.ArrayType(erasedGlb(elem1, elem2, isJava)) - case _ => defn.ObjectType + case JavaArrayType(elem2) => JavaArrayType(erasedGlb(elem1, elem2, isJava)) + case _ => tp1 } case _ => tp2 match { - case defn.ArrayType(_) => defn.ObjectType + case JavaArrayType(_) => tp2 case _ => val tsym1 = tp1.typeSymbol val tsym2 = tp2.typeSymbol @@ -145,14 +169,13 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild /** The erasure |T| of a type T. This is: * * - For a refined type scala.Array+[T]: - * - if T is Nothing or Null, scala.Array+[Object] - * - otherwise, if T <: Object, scala.Array+[|T|] - * - otherwise, if T is a type paramter coming from Java, scala.Array+[Object]. + * - if T is Nothing or Null, []Object + * - otherwise, if T <: Object, []|T| + * - otherwise, if T is a type paramter coming from Java, []Object * - otherwise, Object * - For a term ref p.x, the type # x. * - For a typeref scala.Any, scala.AnyVal, scala.Singleon or scala.NotNull: |java.lang.Object| * - For a typeref scala.Unit, |scala.runtime.BoxedUnit|. - * - For a typeref whose symbol is owned by Array: The typeref itself, with prefix = * - For a typeref P.C where C refers to a class, # C. * - For a typeref P.C where C refers to an alias type, the erasure of C's alias. * - For a typeref P.C where C refers to an abstract type, the erasure of C's upper bound. @@ -175,8 +198,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild def apply(tp: Type)(implicit ctx: Context): Type = tp match { case tp: TypeRef => val sym = tp.symbol - if (!sym.isClass) - if (sym.exists && (sym.owner eq defn.ArrayClass)) tp else this(tp.info) + if (!sym.isClass) this(tp.info) else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tp) else eraseNormalClassRef(tp) case tp: RefinedType => @@ -214,22 +236,25 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild def eraseTypeRef(p: TypeRef) = this(p).asInstanceOf[TypeRef] val parents: List[TypeRef] = if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil - else if (cls eq defn.ArrayClass) defn.ObjectClass.typeRef :: Nil else removeLaterObjects(classParents.mapConserve(eraseTypeRef)) tp.derivedClassInfo(NoPrefix, parents, decls, this(tp.selfType)) // can't replace selftype by NoType because this would lose the sourceModule link } - case NoType | NoPrefix | ErrorType => + case NoType | NoPrefix | ErrorType | JavaArrayType(_) => tp case tp: WildcardType if wildcardOK => tp } - private def eraseArray(tp: RefinedType)(implicit ctx: Context) = { + def eraseArray(tp: RefinedType)(implicit ctx: Context) = { val defn.ArrayType(elemtp) = tp - if (elemtp derivesFrom defn.NullClass) defn.ObjectArrayType - else if (isUnboundedGeneric(elemtp)) defn.ObjectType - else defn.ArrayType(this(elemtp)) + if (elemtp derivesFrom defn.NullClass) JavaArrayType(defn.ObjectType) + else if (isUnboundedGeneric(elemtp)) + elemtp match { + case elemtp: TypeRef if elemtp.symbol.is(JavaDefined) => JavaArrayType(defn.ObjectType) + case _ => defn.ObjectType + } + else JavaArrayType(this(elemtp)) } private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = @@ -277,6 +302,8 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild else if (sym.isDerivedValueClass) sigName(eraseDerivedValueClassRef(tp)) else normalizeClass(sym.asClass).fullName.asTypeName case defn.ArrayType(elem) => + sigName(this(tp)) + case JavaArrayType(elem) => sigName(elem) ++ "[]" case tp: TypeBounds => sigName(tp.hi) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 304d9853c..c53e00152 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -330,7 +330,6 @@ class Definitions { def NothingType: Type = NothingClass.typeRef def NullType: Type = NullClass.typeRef def SeqType: Type = SeqClass.typeRef - def ObjectArrayType = ArrayType(ObjectType) def UnitType: Type = UnitClass.typeRef def BooleanType: Type = BooleanClass.typeRef diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index e7283c827..74673235a 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -196,6 +196,14 @@ object NameOps { case nme.clone_ => nme.array_clone } + /** The name of the primitive runtime operation corresponding to an array operation */ + def primitiveArrayOp: TermName = name match { + case nme.apply => nme.primitive.arrayApply + case nme.length => nme.primitive.arrayLength + case nme.update => nme.primitive.arrayUpdate + case nme.CONSTRUCTOR => nme.primitive.arrayConstructor + } + /** If name length exceeds allowable limit, replace part of it by hash */ def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString)) } diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 91a77a2a8..af4e810de 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -635,6 +635,16 @@ object StdNames { def newBitmapName(bitmapPrefix: TermName, n: Int): TermName = bitmapPrefix ++ n.toString def selectorName(n: Int): TermName = "_" + (n + 1) + + object primitive { + val arrayApply: TermName = "[]apply" + val arrayUpdate: TermName = "[]update" + val arrayLength: TermName = "[]length" + val arrayConstructor: TermName = "[]" + val names: Set[Name] = Set(arrayApply, arrayUpdate, arrayLength, arrayConstructor) + } + + def isPrimitiveName(name: Name) = primitive.names.contains(name) } class ScalaTypeNames extends ScalaNames[TypeName] { diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 660287089..63f998c10 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -924,7 +924,7 @@ object SymDenotations { /** The type parameters of this class */ override final def typeParams(implicit ctx: Context): List[TypeSymbol] = { def computeTypeParams = { - if (ctx.phase.erasedTypes && (symbol ne defn.ArrayClass)) Nil + if (ctx.erasedTypes && (symbol ne defn.ArrayClass)) Nil else if (this ne initial) initial.asSymDenotation.typeParams else decls.filter(sym => (sym is TypeParam) && sym.owner == symbol).asInstanceOf[List[TypeSymbol]] diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index fda9667e9..c9c4595ca 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -735,6 +735,12 @@ class TypeComparer(initctx: Context) extends DotClass { false } compareClassInfo + case JavaArrayType(elem2) => + def compareJavaArray = tp1 match { + case JavaArrayType(elem1) => isSubType(elem1, elem2) + case _ => fourthTry(tp1, tp2) + } + compareJavaArray case _ => fourthTry(tp1, tp2) } @@ -773,6 +779,8 @@ class TypeComparer(initctx: Context) extends DotClass { } || needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _)) case AndType(tp11, tp12) => isNewSubType(tp11, tp2) || isNewSubType(tp12, tp2) + case JavaArrayType(elem1) => + tp2 isRef ObjectClass case _ => false } diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 7669b1a3a..0da9b6d54 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -122,6 +122,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { return "=> " ~ toText(result) case tp: ClassInfo => return toTextParents(tp.instantiatedParents) ~ "{...}" + case JavaArrayType(elemtp) => + return toText(elemtp) ~ "[]" case tp: SelectionProto => return toText(RefinedType(WildcardType, tp.name, tp.memberProto)) case tp: ViewProto => diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 1efc6b53c..86151fae2 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -24,7 +24,7 @@ import scala.collection.mutable.ListBuffer import dotty.tools.dotc.core.Flags import ValueClasses._ import TypeUtils._ -import com.sun.j3d.utils.behaviors.picking.Intersect +import typer.Mode class Erasure extends Phase with DenotTransformer { thisTransformer => @@ -51,10 +51,13 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => val newOwner = if (oldOwner eq defn.AnyClass) defn.ObjectClass else oldOwner val oldInfo = ref.info val newInfo = transformInfo(ref.symbol, oldInfo) - if ((oldOwner eq newOwner) && (oldInfo eq newInfo)) ref + val oldFlags = ref.flags + val newFlags = ref.flags &~ Flags.HasDefaultParams // HasDefaultParams needs to be dropped because overriding might become overloading + // TODO: define derivedSymDenotation? + if ((oldOwner eq newOwner) && (oldInfo eq newInfo) && (oldFlags == newFlags)) ref else { assert(!ref.is(Flags.PackageClass), s"trans $ref @ ${ctx.phase} oldOwner = $oldOwner, newOwner = $newOwner, oldInfo = $oldInfo, newInfo = $newInfo ${oldOwner eq newOwner} ${oldInfo eq newInfo}") - ref.copySymDenotation(owner = newOwner, info = newInfo) + ref.copySymDenotation(owner = newOwner, initFlags = newFlags, info = newInfo) } } case ref => @@ -155,7 +158,7 @@ object Erasure { // assert(!pt.isInstanceOf[SingletonType], pt) if (pt isRef defn.UnitClass) unbox(tree, pt) else (tree.tpe, pt) match { - case (defn.ArrayType(treeElem), defn.ArrayType(ptElem)) + case (JavaArrayType(treeElem), JavaArrayType(ptElem)) if treeElem.widen.isPrimitiveValueType && !ptElem.isPrimitiveValueType => // See SI-2386 for one example of when this might be necessary. cast(ref(defn.runtimeMethod(nme.toObjectArray)).appliedTo(tree), pt) @@ -209,22 +212,24 @@ object Erasure { } /** Type check select nodes, applying the following rewritings exhaustively - * on selections `e.m`. + * on selections `e.m`, where `OT` is the type of the owner of `m` and `ET` + * is the erased type of the selection's original qualifier expression. * - * e.m1 -> e.m2 if `m1` is a member of Any or AnyVal and `m2` is - * the same-named member in Object. - * e.m -> box(e).m if `e` is primitive and `m` is a member or a reference class - * or `e` has an erased value class type. - * e.m -> unbox(e).m if `e` is not primitive and `m` is a member of a primtive type. + * e.m1 -> e.m2 if `m1` is a member of Any or AnyVal and `m2` is + * the same-named member in Object. + * e.m -> box(e).m if `e` is primitive and `m` is a member or a reference class + * or `e` has an erased value class type. + * e.m -> unbox(e).m if `e` is not primitive and `m` is a member of a primtive type. + * e.m -> cast(e, OT).m if the type of `e` does not conform to OT and `m` + * is not an array operation. * - * Additionally, if the type of `e` does not derive from the type `OT` of the owner of `m`, - * the following rewritings are performed, where `ET` is the erased type of the selection's - * original qualifier expression. + * If `m` is an array operation, i.e. one of the members apply, update, length, clone, and + * of class Array, we additionally try the following rewritings: * - * e.m -> cast(OT).m if `m` is not an array operation - * e.m -> cast(ET).m if `m` is an array operation and `ET` is an array type - * e.m -> runtime.array_m(e) - * if `m` is an array operation and `ET` is Object + * e.m -> runtime.array_m(e) if ET is Object + * e.m -> cast(e, ET).m if the type of `e` does not conform to ET + * e.clone -> e.clone' where clone' is Object's clone method + * e.m -> e.[]m if `m` is an array operation other than `clone`. */ override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { val sym = tree.symbol @@ -233,10 +238,15 @@ object Erasure { def select(qual: Tree, sym: Symbol): Tree = untpd.cpy.Select(tree)(qual, sym.name) withType qual.tpe.select(sym) - def selectArrayMember(qual: Tree, erasedPre: Type) = { - if (erasedPre isRef defn.ObjectClass) runtimeCallWithProtoArgs(tree.name.genericArrayOp, pt, qual) - else recur(cast(qual, erasedPre)) - } + def selectArrayMember(qual: Tree, erasedPre: Type): Tree = + if (erasedPre isRef defn.ObjectClass) + runtimeCallWithProtoArgs(tree.name.genericArrayOp, pt, qual) + else if (!(qual.tpe <:< erasedPre)) + selectArrayMember(cast(qual, erasedPre), erasedPre) + else if (sym == defn.Array_clone) + untpd.cpy.Select(tree)(qual, tree.name).withType(defn.Object_clone.termRef) + else + assignType(untpd.cpy.Select(tree)(qual, tree.name.primitiveArrayOp), qual) def recur(qual: Tree): Tree = { val qualIsPrimitive = qual.tpe.widen.isPrimitiveValueType @@ -249,10 +259,10 @@ object Erasure { recur(box(qual)) else if (!qualIsPrimitive && symIsPrimitive) recur(unbox(qual, sym.owner.typeRef)) - else if (qual.tpe.derivesFrom(sym.owner) || qual.isInstanceOf[Super]) - select(qual, sym) else if (sym.owner eq defn.ArrayClass) selectArrayMember(qual, erasure(tree.qualifier.typeOpt.widen.finalResultType)) + else if (qual.tpe.derivesFrom(sym.owner) || qual.isInstanceOf[Super]) + select(qual, sym) else recur(cast(qual, sym.owner.typeRef)) } @@ -274,7 +284,7 @@ object Erasure { override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context) = { val TypeApply(fun, args) = tree - val fun1 = typedExpr(fun, pt) + val fun1 = typedExpr(fun, WildcardType) fun1.tpe.widen match { case funTpe: PolyType => val args1 = args.mapconserve(typedType(_)) @@ -283,19 +293,6 @@ object Erasure { } } -/* - private def contextArgs(tree: untpd.Tree)(implicit ctx: Context): List[untpd.Tree] = { - def nextOuter(ctx: Context): Context = - if (ctx.outer.tree eq tree) nextOuter(ctx.outer) else ctx.outer - ctx.tree match { - case enclApp @ Apply(enclFun, enclArgs) if enclFun eq tree => - enclArgs ++ contextArgs(enclApp)(nextOuter(ctx)) - case _ => - Nil - } - } -*/ - override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { val Apply(fun, args) = tree typedExpr(fun, FunProto(args, pt, this)) match { @@ -425,6 +422,7 @@ object Erasure { ctx.traceIndented(i"adapting ${tree.showSummary}: ${tree.tpe} to $pt", show = true) { assert(ctx.phase == ctx.erasurePhase.next, ctx.phase) if (tree.isEmpty) tree + else if (ctx.mode is Mode.Pattern) tree // TODO: replace with assertion once pattern matcher is active else { val tree1 = adaptToType(tree, pt) tree1.tpe match { diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index f74f4f208..8c9ffb1fb 100644 --- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -86,7 +86,7 @@ class TypeTestsCasts extends MiniPhaseTransform { else derivedTree(box(qual), defn.Any_asInstanceOf, argType) } else if (argCls.isPrimitiveValueClass) - unbox(qual, argType) + unbox(qual.ensureConforms(defn.ObjectType), argType) else derivedTree(qual, defn.Any_asInstanceOf, argType) } diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 4d996fd61..153e0d242 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -5,7 +5,7 @@ package typer import core._ import ast._ import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ -import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._ +import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._ import util.Positions._ import config.Printers._ @@ -183,8 +183,21 @@ trait TypeAssigner { def assignType(tree: untpd.Ident, tp: Type)(implicit ctx: Context) = tree.withType(tp) - def assignType(tree: untpd.Select, qual: Tree)(implicit ctx: Context) = - tree.withType(accessibleSelectionType(tree, qual)) + def assignType(tree: untpd.Select, qual: Tree)(implicit ctx: Context): Select = { + def arrayElemType = { + val JavaArrayType(elemtp) = qual.tpe.widen + elemtp + } + val p = nme.primitive + val tp = tree.name match { + case p.arrayApply => MethodType(defn.IntType :: Nil, arrayElemType) + case p.arrayUpdate => MethodType(defn.IntType :: arrayElemType :: Nil, defn.UnitType) + case p.arrayLength => MethodType(Nil, defn.IntType) + case p.arrayConstructor => MethodType(defn.IntType :: Nil, qual.tpe) + case _ => accessibleSelectionType(tree, qual) + } + tree.withType(tp) + } def assignType(tree: untpd.SelectFromTypeTree, qual: Tree)(implicit ctx: Context) = tree.withType(accessibleSelectionType(tree, qual)) -- cgit v1.2.3