summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2013-10-28 08:57:02 +0100
committerJason Zaugg <jzaugg@gmail.com>2013-10-28 08:57:02 +0100
commit4acac088dcbdce5b6a5b99a31a15c85a2715bbb6 (patch)
tree3163f0e0bdc6c092e59dd893c923bc5af8ad6a1a /src
parent3eee4b7c4419df007a4c089acb4aa6304a9a2970 (diff)
downloadscala-4acac088dcbdce5b6a5b99a31a15c85a2715bbb6.tar.gz
scala-4acac088dcbdce5b6a5b99a31a15c85a2715bbb6.tar.bz2
scala-4acac088dcbdce5b6a5b99a31a15c85a2715bbb6.zip
SI-6385 Avoid bridges to identical signatures over value classes
As Paul noted in the comments to SI-6260 (from which I mined some test cases) "there is no possible basis for conflict here": scala> class C[A](val a: Any) extends AnyVal defined class C scala> class B { def x[A](ca: C[A]) = () } defined class B scala> class D extends B { override def x[A](ca: C[A]) = () } <console>:8: error: bridge generated for member method x: [A](ca: C[A])Unit in class D which overrides method x: [A](ca: C[A])Unit in class B clashes with definition of the member itself; both have erased type (ca: Object)Unit class D extends B { override def x[A](ca: C[A]) = () } ^ What was happening? Bridge computation compares `B#x` and `D#x` exitingErasure, which results in comparing: ErasedValueType(C[A(in B#x)]) =:= ErasedValueType(C[A(in D#x)]) These types were considered distinct (on the grounds of the unique type hash consing), even though they have the same erasure and involve the same value class. That triggered creation of an bridge. After post-erasure eliminates the `ErasedValuedType`s, we find that this marvel of enginineering is bridges `(Object)Unit` right back onto itself. The previous resolution of SI-6385 (d435f72e5fb7fe) was a test case that confirmed that we detected the zero-length bridge and reported it nicely, which happened after related work in SI-6260. But we can simply avoid creating in it in the first place. That's what this commit does. It does so by reducing the amount of information carried in `ErasedValueType` to the bare minimum needed during the erasure -> posterasure transition. We need to know: 1. which value class wraps the value, so we can box and unbox as needed 2. the erasure of the underlying value, which will replace this type in post-erasure. This construction means that the bridge above computation now compares: ErasedValueType(C, Any) =:= ErasedValueType(C, Any]) I have included a test to show that: - we don't incur any linkage or other runtime errors in the reported case (run/t6385.scala) - a similar case compiles when the signatures align (pos/t6260a.scala), but does *not* compile when the just erasures align (neg/t6260c.scala) - polymorphic value classes continue to erase to the instantiated type of the unbox: (run/t6260b.scala) - other cases in SI-6260 remains unsolved and indeed unsolvable without an overhaul of value classes: (neg/t6260b.scala) In my travels I spotted a bug in corner case of null, asInstanceOf and value classes, which I have described in a pending test.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala21
-rw-r--r--src/compiler/scala/tools/nsc/transform/PostErasure.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Constants.scala10
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala21
-rw-r--r--src/reflect/scala/reflect/internal/transform/Erasure.scala4
5 files changed, 34 insertions, 24 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index e2902a74b8..f0d3db1296 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -545,8 +545,7 @@ abstract class Erasure extends AddInterfaces
ldef setType ldef.rhs.tpe
case _ =>
val tree1 = tree.tpe match {
- case ErasedValueType(tref) =>
- val clazz = tref.sym
+ case ErasedValueType(clazz, _) =>
New(clazz, cast(tree, underlyingOfValueClass(clazz)))
case _ =>
tree.tpe.typeSymbol match {
@@ -597,9 +596,7 @@ abstract class Erasure extends AddInterfaces
ldef setType ldef.rhs.tpe
case _ =>
val tree1 = pt match {
- case ErasedValueType(tref) =>
- val clazz = tref.sym
- lazy val underlying = underlyingOfValueClass(clazz)
+ case ErasedValueType(clazz, underlying) =>
val tree0 =
if (tree.tpe.typeSymbol == NullClass &&
isPrimitiveValueClass(underlying.typeSymbol)) {
@@ -697,13 +694,11 @@ abstract class Erasure extends AddInterfaces
case Apply(ta @ TypeApply(sel @ Select(qual, name), List(targ)), List())
if tree.symbol == Any_asInstanceOf =>
val qual1 = typedQualifier(qual, NOmode, ObjectTpe) // need to have an expected type, see #3037
-
+ // !!! Make pending/run/t5866b.scala work. The fix might be here and/or in unbox1.
if (isPrimitiveValueType(targ.tpe) || isErasedValueType(targ.tpe)) {
val noNullCheckNeeded = targ.tpe match {
- case ErasedValueType(tref) =>
- enteringPhase(currentRun.erasurePhase) {
- isPrimitiveValueClass(erasedValueClassArg(tref).typeSymbol)
- }
+ case ErasedValueType(_, underlying) =>
+ isPrimitiveValueClass(underlying.typeSymbol)
case _ =>
true
}
@@ -724,7 +719,7 @@ abstract class Erasure extends AddInterfaces
case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List())
if tree.symbol == Any_isInstanceOf =>
targ.tpe match {
- case ErasedValueType(tref) => targ.setType(tref.sym.tpe)
+ case ErasedValueType(clazz, _) => targ.setType(clazz.tpe)
case _ =>
}
tree
@@ -787,11 +782,11 @@ abstract class Erasure extends AddInterfaces
(tree.attachments.get[TypeRefAttachment]: @unchecked) match {
case Some(itype) =>
val tref = itype.tpe
- val argPt = enteringPhase(currentRun.erasurePhase)(erasedValueClassArg(tref))
+ val argPt = enteringErasure(erasedValueClassArg(tref))
log(s"transforming inject $arg -> $tref/$argPt")
val result = typed(arg, mode, argPt)
log(s"transformed inject $arg -> $tref/$argPt = $result:${result.tpe}")
- return result setType ErasedValueType(tref)
+ return result setType ErasedValueType(tref.sym, result.tpe)
}
case _ =>
diff --git a/src/compiler/scala/tools/nsc/transform/PostErasure.scala b/src/compiler/scala/tools/nsc/transform/PostErasure.scala
index 96263f3c0c..cc78e27282 100644
--- a/src/compiler/scala/tools/nsc/transform/PostErasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/PostErasure.scala
@@ -22,7 +22,7 @@ trait PostErasure extends InfoTransform with TypingTransformers {
object elimErasedValueType extends TypeMap {
def apply(tp: Type) = tp match {
case ConstantType(Constant(tp: Type)) => ConstantType(Constant(apply(tp)))
- case ErasedValueType(tref) => enteringErasure(erasure.erasedValueClassArg(tref))
+ case ErasedValueType(_, underlying) => underlying
case _ => mapOver(tp)
}
}
diff --git a/src/reflect/scala/reflect/internal/Constants.scala b/src/reflect/scala/reflect/internal/Constants.scala
index 511b39b8c6..85d0efdcba 100644
--- a/src/reflect/scala/reflect/internal/Constants.scala
+++ b/src/reflect/scala/reflect/internal/Constants.scala
@@ -223,7 +223,15 @@ trait Constants extends api.Constants {
case ClazzTag =>
def show(tpe: Type) = "classOf[" + signature(tpe) + "]"
typeValue match {
- case ErasedValueType(orig) => show(orig)
+ case ErasedValueType(clazz, underlying) =>
+ // A note on tpe_* usage here:
+ //
+ // We've intentionally erased the type arguments to the value class so that different
+ // instantiations of a particular value class that erase to the same underlying type
+ // don't result in spurious bridges (e.g. run/t6385.scala). I don't think that matters;
+ // printing trees of `classOf[ValueClass[String]]` shows `classOf[ValueClass]` at phase
+ // erasure both before and after the use of `tpe_*` here.
+ show(clazz.tpe_*)
case _ => show(typeValue)
}
case CharTag => "'" + escapedChar(charValue) + "'"
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index 59d1e13142..653030e602 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -3341,18 +3341,25 @@ trait Types
/** A temporary type representing the erasure of a user-defined value type.
* Created during phase erasure, eliminated again in posterasure.
*
- * @param original The underlying type before erasure
+ * SI-6385 Erasure's creation of bridges considers method signatures `exitingErasure`,
+ * which contain `ErasedValueType`-s. In order to correctly consider the overriding
+ * and overriden signatures as equivalent in `run/t6385.scala`, it is critical that
+ * this type contains the erasure of the wrapped type, rather than the unerased type
+ * of the value class itself, as was originally done.
+ *
+ * @param valueClazz The value class symbol
+ * @param erasedUnderlying The erased type of the unboxed value
*/
- abstract case class ErasedValueType(original: TypeRef) extends UniqueType {
- override def safeToString = "ErasedValueType("+original+")"
+ abstract case class ErasedValueType(valueClazz: Symbol, erasedUnderlying: Type) extends UniqueType {
+ override def safeToString = s"ErasedValueType($valueClazz, $erasedUnderlying)"
}
- final class UniqueErasedValueType(original: TypeRef) extends ErasedValueType(original)
+ final class UniqueErasedValueType(valueClazz: Symbol, erasedUnderlying: Type) extends ErasedValueType(valueClazz, erasedUnderlying)
object ErasedValueType {
- def apply(original: TypeRef): Type = {
- assert(original.sym ne NoSymbol, "ErasedValueType over NoSymbol")
- unique(new UniqueErasedValueType(original))
+ def apply(valueClazz: Symbol, erasedUnderlying: Type): Type = {
+ assert(valueClazz ne NoSymbol, "ErasedValueType over NoSymbol")
+ unique(new UniqueErasedValueType(valueClazz, erasedUnderlying))
}
}
diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala
index 185e2c3d1e..4aefc105e3 100644
--- a/src/reflect/scala/reflect/internal/transform/Erasure.scala
+++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala
@@ -257,11 +257,11 @@ trait Erasure {
/** This is used as the Scala erasure during the erasure phase itself
* It differs from normal erasure in that value classes are erased to ErasedValueTypes which
- * are then later converted to the underlying parameter type in phase posterasure.
+ * are then later unwrapped to the underlying parameter type in phase posterasure.
*/
object specialScalaErasure extends ScalaErasureMap {
override def eraseDerivedValueClassRef(tref: TypeRef): Type =
- ErasedValueType(tref)
+ ErasedValueType(tref.sym, erasedValueClassArg(tref))
}
object javaErasure extends JavaErasureMap