summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2012-02-08 16:29:20 +0100
committerMartin Odersky <odersky@gmail.com>2012-02-08 16:29:20 +0100
commit26348f45421e2ab31fa707146d6ca6b42a8157cc (patch)
tree3d7ffe0f7cde3168699e04506ed456bdd78f8b75 /src
parentb925feb273783e45eeb71afee65c53210c76a056 (diff)
downloadscala-26348f45421e2ab31fa707146d6ca6b42a8157cc.tar.gz
scala-26348f45421e2ab31fa707146d6ca6b42a8157cc.tar.bz2
scala-26348f45421e2ab31fa707146d6ca6b42a8157cc.zip
wip. Getting started on erasing inline classes.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/reflect/internal/Symbols.scala3
-rw-r--r--src/compiler/scala/reflect/internal/Types.scala4
-rw-r--r--src/compiler/scala/reflect/internal/transform/Erasure.scala51
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala171
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala6
5 files changed, 150 insertions, 85 deletions
diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala
index f58b35fead..e448ef86f6 100644
--- a/src/compiler/scala/reflect/internal/Symbols.scala
+++ b/src/compiler/scala/reflect/internal/Symbols.scala
@@ -502,6 +502,9 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
final def isInlineClass =
isClass && info.parents.headOption.getOrElse(AnyClass.tpe).typeSymbol == AnyValClass &&
!isPrimitiveValueClass
+
+ final def isMethodWithExtension =
+ isMethod && owner.isInlineClass && !isParamAccessor && !isConstructor
final def isAnonymousClass = isClass && (name containsName tpnme.ANON_CLASS_NAME)
final def isAnonymousFunction = isSynthetic && (name containsName tpnme.ANON_FUN_NAME)
diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala
index 0b5816a029..a93f8ea683 100644
--- a/src/compiler/scala/reflect/internal/Types.scala
+++ b/src/compiler/scala/reflect/internal/Types.scala
@@ -3076,7 +3076,9 @@ trait Types extends api.Types { self: SymbolTable =>
}
}
- abstract case class ErasedInlineType(sym: Symbol) extends Type
+ abstract case class ErasedInlineType(sym: Symbol) extends Type {
+ override def safeToString = sym.name+"$unboxed"
+ }
final class UniqueErasedInlineType(sym: Symbol) extends ErasedInlineType(sym) with UniqueType
diff --git a/src/compiler/scala/reflect/internal/transform/Erasure.scala b/src/compiler/scala/reflect/internal/transform/Erasure.scala
index b22f18ef75..f0981d7141 100644
--- a/src/compiler/scala/reflect/internal/transform/Erasure.scala
+++ b/src/compiler/scala/reflect/internal/transform/Erasure.scala
@@ -2,6 +2,8 @@ package scala.reflect
package internal
package transform
+import Flags.PARAMACCESSOR
+
trait Erasure {
val global: SymbolTable
@@ -63,16 +65,21 @@ trait Erasure {
if (cls.owner.isClass) cls.owner.tpe else pre // why not cls.isNestedClass?
}
- protected def unboxInlineType(clazz: Symbol): Type =
+ protected def valueClassErasure(clazz: Symbol): Type =
clazz.primaryConstructor.info.params.head.tpe
-
- protected def eraseInlineClassRef(clazz: Symbol): Type = {
- scalaErasure(unboxInlineType(clazz))
- }
-
+
+ protected def eraseInlineClassRef(clazz: Symbol): Type =
+ scalaErasure(valueClassErasure(clazz))
+
+ protected def underlyingParamAccessor(clazz: Symbol) =
+ clazz.info.decls.find(_ hasFlag PARAMACCESSOR).get
+
abstract class ErasureMap extends TypeMap {
def mergeParents(parents: List[Type]): Type
+ def eraseNormalClassRef(pre: Type, clazz: Symbol): Type =
+ typeRef(apply(rebindInnerClass(pre, clazz)), clazz, List()) // #2585
+
def apply(tp: Type): Type = {
tp match {
case ConstantType(_) =>
@@ -87,8 +94,8 @@ trait Erasure {
else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == NotNullClass) erasedTypeRef(ObjectClass)
else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass)
else if (sym.isRefinementClass) apply(mergeParents(tp.parents))
- //else if (sym.isInlineClass) eraseInlineClassRef(sym)
- else if (sym.isClass) typeRef(apply(rebindInnerClass(pre, sym)), sym, List()) // #2585
+ else if (sym.isInlineClass) eraseInlineClassRef(sym)
+ else if (sym.isClass) eraseNormalClassRef(pre, sym)
else apply(sym.info) // alias type or abstract type
case PolyType(tparams, restpe) =>
apply(restpe)
@@ -155,8 +162,14 @@ trait Erasure {
log("Identified divergence between java/scala erasure:\n scala: " + old + "\n java: " + res)
}
res
- }
- else scalaErasure(tp)
+ } else if (sym.isMethodWithExtension || sym.isConstructor && sym.owner.isInlineClass)
+ scalaErasureAvoiding(sym.owner, tp)
+ else if (sym.isValue && sym.owner.isMethodWithExtension)
+ scala.tools.nsc.util.trace("avoid unboxed: "+sym+"/"+sym.owner.owner+"/"+tp) {
+ scalaErasureAvoiding(sym.owner.owner, tp)
+ }
+ else
+ scalaErasure(tp)
}
/** Scala's more precise erasure than java's is problematic as follows:
@@ -178,6 +191,24 @@ trait Erasure {
def mergeParents(parents: List[Type]): Type =
intersectionDominator(parents)
}
+
+ def scalaErasureAvoiding(clazz: Symbol, tpe: Type): Type = {
+ tpe match {
+ case PolyType(tparams, restpe) =>
+ scalaErasureAvoiding(clazz, restpe)
+ case ExistentialType(tparams, restpe) =>
+ scalaErasureAvoiding(clazz, restpe)
+ case mt @ MethodType(params, restpe) =>
+ MethodType(
+ cloneSymbolsAndModify(params, scalaErasureAvoiding(clazz, _)),
+ if (restpe.typeSymbol == UnitClass) erasedTypeRef(UnitClass)
+ else scalaErasureAvoiding(clazz, (mt.resultType(params map (_.tpe)))))
+ case TypeRef(pre, `clazz`, args) =>
+ scalaErasure.eraseNormalClassRef(pre, clazz)
+ case _ =>
+ scalaErasure(tpe)
+ }
+ }
/** The intersection dominator (SLS 3.7) of a list of types is computed as follows.
*
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index 750a8700be..fa64fbf48b 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -394,6 +394,14 @@ abstract class Erasure extends AddInterfaces
class Eraser(_context: Context) extends Typer(_context) {
private def safeToRemoveUnbox(cls: Symbol): Boolean =
(cls == definitions.NullClass) || isBoxedValueClass(cls)
+
+ private def isUnboxedType(tpe: Type) = tpe match {
+ case ErasedInlineType(_) => true
+ case _ => isPrimitiveValueClass(tpe.typeSymbol)
+ }
+
+ private def isUnboxedValueMember(sym: Symbol) =
+ sym != NoSymbol && isPrimitiveValueClass(sym.owner)
/** Box `tree` of unboxed type */
private def box(tree: Tree): Tree = tree match {
@@ -401,27 +409,35 @@ abstract class Erasure extends AddInterfaces
val rhs1 = box(rhs)
treeCopy.LabelDef(tree, name, params, rhs1) setType rhs1.tpe
case _ =>
- typedPos(tree.pos)(tree.tpe.typeSymbol match {
- case UnitClass =>
- if (treeInfo isExprSafeToInline tree) REF(BoxedUnit_UNIT)
- else BLOCK(tree, REF(BoxedUnit_UNIT))
- case NothingClass => tree // a non-terminating expression doesn't need boxing
- case x =>
- assert(x != ArrayClass)
- tree match {
- /** Can't always remove a Box(Unbox(x)) combination because the process of boxing x
- * may lead to throwing an exception.
- *
- * This is important for specialization: calls to the super constructor should not box/unbox specialized
- * fields (see TupleX). (ID)
- */
- case Apply(boxFun, List(arg)) if isUnbox(tree.symbol) && safeToRemoveUnbox(arg.tpe.typeSymbol) =>
- log("boxing an unbox: " + tree + " and replying with " + arg)
- arg
- case _ =>
- (REF(boxMethod(x)) APPLY tree) setPos (tree.pos) setType ObjectClass.tpe
+ val tree1 = tree.tpe match {
+ case ErasedInlineType(clazz) =>
+ util.trace("converting "+tree.tpe+" to "+valueClassErasure(clazz)+":")(
+ New(clazz, cast(tree, valueClassErasure(clazz)))
+ )
+ case _ =>
+ tree.tpe.typeSymbol match {
+ case UnitClass =>
+ if (treeInfo isExprSafeToInline tree) REF(BoxedUnit_UNIT)
+ else BLOCK(tree, REF(BoxedUnit_UNIT))
+ case NothingClass => tree // a non-terminating expression doesn't need boxing
+ case x =>
+ assert(x != ArrayClass)
+ tree match {
+ /** Can't always remove a Box(Unbox(x)) combination because the process of boxing x
+ * may lead to throwing an exception.
+ *
+ * This is important for specialization: calls to the super constructor should not box/unbox specialized
+ * fields (see TupleX). (ID)
+ */
+ case Apply(boxFun, List(arg)) if isUnbox(tree.symbol) && safeToRemoveUnbox(arg.tpe.typeSymbol) =>
+ log("boxing an unbox: " + tree + " and replying with " + arg)
+ arg
+ case _ =>
+ (REF(boxMethod(x)) APPLY tree) setPos (tree.pos) setType ObjectClass.tpe
+ }
}
- })
+ }
+ typedPos(tree.pos)(tree1)
}
/** Unbox `tree` of boxed type to expected type `pt`.
@@ -440,15 +456,22 @@ abstract class Erasure extends AddInterfaces
val rhs1 = unbox(rhs, pt)
treeCopy.LabelDef(tree, name, params, rhs1) setType rhs1.tpe
case _ =>
- typedPos(tree.pos)(pt.typeSymbol match {
- case UnitClass =>
- if (treeInfo isExprSafeToInline tree) UNIT
- else BLOCK(tree, UNIT)
- case x =>
- assert(x != ArrayClass)
- // don't `setType pt` the Apply tree, as the Apply's fun won't be typechecked if the Apply tree already has a type
- Apply(unboxMethod(pt.typeSymbol), tree)
- })
+ val tree1 = pt match {
+ case ErasedInlineType(clazz) =>
+ val tree0 = adaptToType(tree, valueClassErasure(clazz))
+ cast(Apply(Select(tree0, underlyingParamAccessor(clazz)), List()), pt)
+ case _ =>
+ pt.typeSymbol match {
+ case UnitClass =>
+ if (treeInfo isExprSafeToInline tree) UNIT
+ else BLOCK(tree, UNIT)
+ case x =>
+ assert(x != ArrayClass)
+ // don't `setType pt` the Apply tree, as the Apply's fun won't be typechecked if the Apply tree already has a type
+ Apply(unboxMethod(pt.typeSymbol), tree)
+ }
+ }
+ typedPos(tree.pos)(tree1)
}
/** Generate a synthetic cast operation from tree.tpe to pt.
@@ -463,9 +486,6 @@ abstract class Erasure extends AddInterfaces
else gen.mkAttributedCast(tree, pt)
}
- private def isUnboxedValueMember(sym: Symbol) =
- sym != NoSymbol && isPrimitiveValueClass(sym.owner)
-
/** Adapt `tree` to expected type `pt`.
*
* @param tree the given tree
@@ -477,14 +497,16 @@ abstract class Erasure extends AddInterfaces
log("adapting " + tree + ":" + tree.tpe + " : " + tree.tpe.parents + " to " + pt)//debug
if (tree.tpe <:< pt)
tree
- else if (isPrimitiveValueClass(tree.tpe.typeSymbol) && !isPrimitiveValueClass(pt.typeSymbol))
- adaptToType(box(tree), pt)
- else if (tree.tpe.isInstanceOf[MethodType] && tree.tpe.params.isEmpty) {
+ else if (isUnboxedType(tree.tpe) && !isUnboxedType(pt)) {
+ val tree1 = util.trace("boxing "+tree.tpe+" to "+pt+" = ")(box(tree))
+ println(tree1.tpe)
+ adaptToType(tree1, pt)
+ } else if (tree.tpe.isInstanceOf[MethodType] && tree.tpe.params.isEmpty) {
assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt)
adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt)
} else if (pt <:< tree.tpe)
cast(tree, pt)
- else if (isPrimitiveValueClass(pt.typeSymbol) && !isPrimitiveValueClass(tree.tpe.typeSymbol))
+ else if (isUnboxedType(pt) && !isUnboxedType(tree.tpe))
adaptToType(unbox(tree, pt), pt)
else
cast(tree, pt)
@@ -500,6 +522,7 @@ abstract class Erasure extends AddInterfaces
* - `x != y` for != in class Any becomes `!(x equals y)` with equals in class Object.
* - x.asInstanceOf[T] becomes x.$asInstanceOf[T]
* - x.isInstanceOf[T] becomes x.$isInstanceOf[T]
+ * - x.isInstanceOf[ErasedInlineType(clazz)] becomes x.isInstanceOf[clazz.tpe]
* - x.m where m is some other member of Any becomes x.m where m is a member of class Object.
* - x.m where x has unboxed value type T and m is not a directly translated member of T becomes T.box(x).m
* - x.m where x is a reference type and m is a directly translated member of value type T becomes x.TValue().m
@@ -512,46 +535,52 @@ abstract class Erasure extends AddInterfaces
case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) if tree.symbol == Any_asInstanceOf =>
val qual1 = typedQualifier(qual, NOmode, ObjectClass.tpe) // need to have an expected type, see #3037
val qualClass = qual1.tpe.typeSymbol
- val targClass = targ.tpe.typeSymbol
/*
+ val targClass = targ.tpe.typeSymbol
+
if (isNumericValueClass(qualClass) && isNumericValueClass(targClass))
// convert numeric type casts
atPos(tree.pos)(Apply(Select(qual1, "to" + targClass.name), List()))
else
*/
- if (isPrimitiveValueClass(targClass)) unbox(qual1, targ.tpe)
+ if (isUnboxedType(targ.tpe)) unbox(qual1, targ.tpe)
else tree
+ case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) if tree.symbol == Any_isInstanceOf =>
+ targ.tpe match {
+ case ErasedInlineType(clazz) => targ.setType(scalaErasure(clazz.tpe))
+ case _ =>
+ }
+ tree
case Select(qual, name) =>
- if (name == nme.CONSTRUCTOR) {
+ if (tree.symbol == NoSymbol) {
+ tree
+ } else if (name == nme.CONSTRUCTOR) {
if (tree.symbol.owner == AnyValClass) tree.symbol = ObjectClass.primaryConstructor
tree
- } else {
- if (tree.symbol == NoSymbol)
- tree
- else if (tree.symbol == Any_asInstanceOf)
- adaptMember(atPos(tree.pos)(Select(qual, Object_asInstanceOf)))
- else if (tree.symbol == Any_isInstanceOf)
- adaptMember(atPos(tree.pos)(Select(qual, Object_isInstanceOf)))
- else if (tree.symbol.owner == AnyClass)
- adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, name))))
- else {
- var qual1 = typedQualifier(qual)
- if ((isPrimitiveValueClass(qual1.tpe.typeSymbol) && !isUnboxedValueMember(tree.symbol)))
- qual1 = box(qual1)
- else if (!isPrimitiveValueClass(qual1.tpe.typeSymbol) && isUnboxedValueMember(tree.symbol))
- qual1 = unbox(qual1, tree.symbol.owner.tpe)
-
- if (isPrimitiveValueClass(tree.symbol.owner) && !isPrimitiveValueClass(qual1.tpe.typeSymbol))
- tree.symbol = NoSymbol
- else if (qual1.tpe.isInstanceOf[MethodType] && qual1.tpe.params.isEmpty) {
- assert(qual1.symbol.isStable, qual1.symbol);
- qual1 = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType
- } else if (!(qual1.isInstanceOf[Super] || (qual1.tpe.typeSymbol isSubClass tree.symbol.owner))) {
- assert(tree.symbol.owner != ArrayClass)
- qual1 = cast(qual1, tree.symbol.owner.tpe)
- }
- treeCopy.Select(tree, qual1, name)
+ } else if (tree.symbol == Any_asInstanceOf)
+ adaptMember(atPos(tree.pos)(Select(qual, Object_asInstanceOf)))
+ else if (tree.symbol == Any_isInstanceOf)
+ adaptMember(atPos(tree.pos)(Select(qual, Object_isInstanceOf)))
+ else if (tree.symbol.owner == AnyClass)
+ adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, name))))
+ else {
+ var qual1 = typedQualifier(qual)
+ if ((isPrimitiveValueClass(qual1.tpe.typeSymbol) && !isUnboxedValueMember(tree.symbol))) {
+ println("boxing "+qual1.tpe+" to member "+tree.symbol)
+ qual1 = box(qual1)
+ } else if (!isPrimitiveValueClass(qual1.tpe.typeSymbol) && isUnboxedValueMember(tree.symbol))
+ qual1 = unbox(qual1, tree.symbol.owner.tpe)
+
+ if (isPrimitiveValueClass(tree.symbol.owner) && !isPrimitiveValueClass(qual1.tpe.typeSymbol))
+ tree.symbol = NoSymbol
+ else if (qual1.tpe.isInstanceOf[MethodType] && qual1.tpe.params.isEmpty) {
+ assert(qual1.symbol.isStable, qual1.symbol);
+ qual1 = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType
+ } else if (!(qual1.isInstanceOf[Super] || (qual1.tpe.typeSymbol isSubClass tree.symbol.owner))) {
+ assert(tree.symbol.owner != ArrayClass)
+ qual1 = cast(qual1, tree.symbol.owner.tpe)
}
+ treeCopy.Select(tree, qual1, name)
}
case SelectFromArray(qual, name, erasure) =>
var qual1 = typedQualifier(qual)
@@ -823,6 +852,7 @@ abstract class Erasure extends AddInterfaces
* - Given a selection q.s, where the owner of `s` is not accessible but the
* type symbol of q's type qT is accessible, insert a cast (q.asInstanceOf[qT]).s
* This prevents illegal access errors (see #4283).
+ * - Remove all instance creations new C(arg) where C is an inlined class.
* - Reset all other type attributes to null, thus enforcing a retyping.
*/
private val preTransformer = new TypingTransformer(unit) {
@@ -905,6 +935,8 @@ abstract class Erasure extends AddInterfaces
else
tree
+ case Apply(Select(New(tpt), nme.CONSTRUCTOR), List(arg)) if (tpt.tpe.typeSymbol.isInlineClass) =>
+ arg
case Apply(fn, args) =>
def qualifier = fn match {
case Select(qual, _) => qual
@@ -961,12 +993,12 @@ abstract class Erasure extends AddInterfaces
}
} else if (fn.symbol.owner.isRefinementClass && !fn.symbol.isOverridingSymbol) {
ApplyDynamic(qualifier, args) setSymbol fn.symbol setPos tree.pos
- } else if (fn.symbol.owner.isInlineClass && extensionMethods.hasExtension(fn.symbol)) {
+ } else if (fn.symbol.isMethodWithExtension) {
Apply(gen.mkAttributedRef(extensionMethods.extensionMethod(fn.symbol)), qualifier :: args)
} else {
- tree
- }
-
+ tree
+ }
+
case Select(qual, name) =>
val owner = tree.symbol.owner
// println("preXform: "+ (tree, tree.symbol, tree.symbol.owner, tree.symbol.owner.isRefinementClass))
@@ -1033,6 +1065,7 @@ abstract class Erasure extends AddInterfaces
*/
override def transform(tree: Tree): Tree = {
val tree1 = preTransformer.transform(tree)
+ println("tree after pretransform: "+tree1)
atPhase(phase.next) {
val tree2 = mixinTransformer.transform(tree1)
debuglog("tree after addinterfaces: \n" + tree2)
diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
index 4db05f46d3..114ec721d8 100644
--- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
@@ -33,9 +33,6 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
def newTransformer(unit: CompilationUnit): Transformer =
new Extender(unit)
- def hasExtension(sym: Symbol) =
- !sym.isParamAccessor && !sym.isConstructor
-
/** Generate stream of possible names for the extension version of given instance method `imeth`.
* If the method is not overloaded, this stream consists of just "extension$imeth".
* If the method is overloaded, the stream has as first element "extensionX$imeth", where X is the
@@ -109,8 +106,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
super.transform(tree)
}
else tree
- case DefDef(mods, name, tparams, vparamss, tpt, rhs)
- if currentOwner.isInlineClass && hasExtension(tree.symbol) =>
+ case DefDef(mods, name, tparams, vparamss, tpt, rhs) if tree.symbol.isMethodWithExtension =>
val companion = currentOwner.companionModule
val origMeth = tree.symbol
val extensionName = extensionNames(origMeth).head