Skip to content

Fix array creation v2 #254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 1, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/dotty/runtime/Arrays.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package dotty.runtime

import scala.reflect.ClassTag

/** All but the first operation should be short-circuited and implemented specially by
* the backend.
*/
object Arrays {

/** Creates an array of some element type determined by the given `ClassTag`
* argument. The erased type of applications of this method is `Object`.
*/
def newGenericArray[T](length: Int)(implicit tag: ClassTag[T]): Array[T] =
tag.newArray(length)

/** Create an array of type T. T must be of form Array[E], with
* E being a reference type.
*/
def newRefArray[T](length: Int): T = ???
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is return type T instead of Array[T] here intentional?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the comment: "T must be of form Array[E]". So yes, I suppose it is intentional.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's intentional. It's a technically to fit best with the way erasure works for polymorphic functions. That way, we can treat newRefArray and asInstanceOf in exactly the same way.


/** Create a Byte[] array */
def newByteArray(length: Int): Array[Byte] = ???

/** Create a Short[] array */
def newShortArray(length: Int): Array[Short] = ???

/** Create a Char[] array */
def newCharArray(length: Int): Array[Char] = ???

/** Create an Int[] array */
def newIntArray(length: Int): Array[Int] = ???

/** Create a Long[] array */
def newLongArray(length: Int): Array[Long] = ???

/** Create a Float[] array */
def newFloatArray(length: Int): Array[Float] = ???

/** Create a Double[] array */
def newDoubleArray(length: Int): Array[Double] = ???

/** Create a Boolean[] array */
def newBooleanArray(length: Int): Array[Boolean] = ???

/** Create a scala.runtime.BoxedUnit[] array */
def newUnitArray(length: Int): Array[Unit] = ???
}
24 changes: 19 additions & 5 deletions src/dotty/tools/dotc/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ object TypeErasure {
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])
if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf[PolyType])
else if (sym.isAbstractType) TypeAlias(WildcardType)
else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx))
else eraseInfo(tp)(erasureCtx) match {
Expand All @@ -153,10 +153,24 @@ object TypeErasure {
}
}

def isUnboundedGeneric(tp: Type)(implicit ctx: Context) = !(
(tp derivesFrom defn.ObjectClass) ||
tp.classSymbol.isPrimitiveValueClass ||
(tp.typeSymbol is JavaDefined))
/** Is `tp` an abstract type or polymorphic type parameter that has `Any`
* as upper bound and that is not Java defined? Arrays of such types are
* erased to `Object` instead of `ObjectArray`.
*/
def isUnboundedGeneric(tp: Type)(implicit ctx: Context): Boolean = tp match {
case tp: TypeRef =>
tp.symbol.isAbstractType &&
!tp.derivesFrom(defn.ObjectClass) &&
!tp.typeSymbol.is(JavaDefined)
case tp: PolyParam =>
!tp.derivesFrom(defn.ObjectClass) &&
!tp.binder.resultType.isInstanceOf[JavaMethodType]
case tp: TypeProxy => isUnboundedGeneric(tp.underlying)
case tp: AndType => isUnboundedGeneric(tp.tp1) || isUnboundedGeneric(tp.tp2)
case tp: OrType => isUnboundedGeneric(tp.tp1) && isUnboundedGeneric(tp.tp2)
case _ => false
}


/** The erased least upper bound is computed as follows
* - if both argument are arrays, an array of the lub of the element types
Expand Down
20 changes: 19 additions & 1 deletion src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,24 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
case ConstantType(value) => Literal(value)
}

/** A tree representing a `newXYZArray` operation of the right
* kind for the given element type in `typeArg`. No type arguments or
* `length` arguments are given.
*/
def newArray(typeArg: Tree, pos: Position)(implicit ctx: Context): Tree = {
val elemType = typeArg.tpe
val elemClass = elemType.classSymbol
def newArr(kind: String) =
ref(defn.DottyArraysModule).select(s"new${kind}Array".toTermName).withPos(pos)
if (TypeErasure.isUnboundedGeneric(elemType))
newArr("Generic").appliedToTypeTrees(typeArg :: Nil)
else if (elemClass.isPrimitiveValueClass)
newArr(elemClass.name.toString)
else
newArr("Ref").appliedToTypeTrees(
TypeTree(defn.ArrayType(elemType)).withPos(typeArg.pos) :: Nil)
}

// ------ Creating typed equivalents of trees that exist only in untyped form -------

/** new C(args), calling the primary constructor of C */
Expand Down Expand Up @@ -678,7 +696,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
Throw(New(defn.ClassCastExceptionClass.typeRef, Nil)) withPos tree.pos
}
}

def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type], expectedType: Type, isAnnotConstructor: Boolean = false)(implicit ctx: Context): Tree = {
val typer = ctx.typer
val proto = new FunProtoTyped(args, expectedType, typer)
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ object Contexts {
protected def scope_=(scope: Scope) = _scope = scope
def scope: Scope = _scope

/** The current type assigner ot typer */
/** The current type assigner or typer */
private[this] var _typeAssigner: TypeAssigner = _
protected def typeAssigner_=(typeAssigner: TypeAssigner) = _typeAssigner = typeAssigner
def typeAssigner: TypeAssigner = _typeAssigner
Expand Down
7 changes: 7 additions & 0 deletions src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ class Definitions {
def staticsMethod(name: PreName) = ctx.requiredMethod(ScalaStaticsClass, name)

lazy val DottyPredefModule = ctx.requiredModule("dotty.DottyPredef")
lazy val DottyArraysModule = ctx.requiredModule("dotty.runtime.Arrays")

def newRefArrayMethod = ctx.requiredMethod(DottyArraysModule.moduleClass.asClass, "newRefArray")

lazy val NilModule = ctx.requiredModule("scala.collection.immutable.Nil")
lazy val PredefConformsClass = ctx.requiredClass("scala.Predef." + tpnme.Conforms)

Expand All @@ -211,6 +215,7 @@ class Definitions {
lazy val Array_update = ctx.requiredMethod(ArrayClass, nme.update)
lazy val Array_length = ctx.requiredMethod(ArrayClass, nme.length)
lazy val Array_clone = ctx.requiredMethod(ArrayClass, nme.clone_)
lazy val ArrayConstructor = ctx.requiredMethod(ArrayClass, nme.CONSTRUCTOR)
lazy val traversableDropMethod = ctx.requiredMethod(ScalaRuntimeClass, nme.drop)
lazy val uncheckedStableClass: ClassSymbol = ctx.requiredClass("scala.annotation.unchecked.uncheckedStable")

Expand Down Expand Up @@ -428,6 +433,8 @@ class Definitions {

lazy val PhantomClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass)

lazy val isPolymorphicAfterErasure = Set[Symbol](Any_isInstanceOf, Any_asInstanceOf, newRefArrayMethod)

lazy val RootImports = List[Symbol](JavaLangPackageVal, ScalaPackageVal, ScalaPredefModule, DottyPredefModule)

lazy val overriddenBySynthetic = Set[Symbol](Any_equals, Any_hashCode, Any_toString, Product_canEqual)
Expand Down
1 change: 0 additions & 1 deletion src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ object NameOps {
case nme.length => nme.primitive.arrayLength
case nme.update => nme.primitive.arrayUpdate
case nme.clone_ => nme.clone_
case nme.CONSTRUCTOR => nme.primitive.arrayConstructor
}

/** If name length exceeds allowable limit, replace part of it by hash */
Expand Down
4 changes: 1 addition & 3 deletions src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,6 @@ object StdNames {
val moduleClass : N = "moduleClass"
val name: N = "name"
val ne: N = "ne"
val newArray: N = "newArray"
val newFreeTerm: N = "newFreeTerm"
val newFreeType: N = "newFreeType"
val newNestedSymbol: N = "newNestedSymbol"
Expand Down Expand Up @@ -691,8 +690,7 @@ object StdNames {
val arrayApply: TermName = "[]apply"
val arrayUpdate: TermName = "[]update"
val arrayLength: TermName = "[]length"
val arrayConstructor: TermName = "[]<init>"
val names: Set[Name] = Set(arrayApply, arrayUpdate, arrayLength, arrayConstructor)
val names: Set[Name] = Set(arrayApply, arrayUpdate, arrayLength)
}

def isPrimitiveName(name: Name) = primitive.names.contains(name)
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class Erasure extends Phase with DenotTransformer { thisTransformer =>
*/
def assertErased(tree: tpd.Tree)(implicit ctx: Context): Unit = {
assertErased(tree.typeOpt, tree)
if (!(tree.symbol == defn.Any_isInstanceOf || tree.symbol == defn.Any_asInstanceOf))
if (!defn.isPolymorphicAfterErasure(tree.symbol))
assertErased(tree.typeOpt.widen, tree)
if (ctx.mode.isExpr)
tree.tpe match {
Expand Down
11 changes: 10 additions & 1 deletion src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,16 @@ trait Applications extends Compatibility { self: Typer =>
checkBounds(typedArgs, pt)
case _ =>
}
assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
convertNewArray(
assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs))
}

/** Rewrite `new Array[T]` trees to calls of newXYZArray methods. */
def convertNewArray(tree: Tree)(implicit ctx: Context): Tree = tree match {
case TypeApply(tycon, targs) if tycon.symbol == defn.ArrayConstructor =>
newArray(targs.head, tree.pos)
case _ =>
tree
}

def typedUnApply(tree: untpd.Apply, selType: Type)(implicit ctx: Context): Tree = track("typedUnApply") {
Expand Down
1 change: 0 additions & 1 deletion src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@ trait TypeAssigner {
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, qualType)
case nme.clone_ if qualType.isInstanceOf[JavaArrayType] => MethodType(Nil, qualType)
case _ => accessibleSelectionType(tree, qual)
}
Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1326,7 +1326,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
if (pt.isInstanceOf[PolyProto]) tree
else {
val (_, tvars) = constrained(poly, tree)
adaptInterpolated(tree appliedToTypes tvars, pt, original)
convertNewArray(
adaptInterpolated(tree.appliedToTypes(tvars), pt, original))
}
case wtp =>
pt match {
Expand Down
14 changes: 14 additions & 0 deletions tests/pos/new-array.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
object Test {
val w = new Array[String](10)
val x = new Array[Int](10)
def f[T: reflect.ClassTag] = new Array[T](10)
val y = new Array[Any](10)
val z = new Array[Unit](10)
}
object Test2 {
val w: Array[String] = new Array(10)
val x: Array[Int] = new Array(10)
def f[T: reflect.ClassTag]: Array[T] = new Array(10)
val y: Array[Any] = new Array(10)
val z: Array[Unit] = new Array(10)
}