diff options
author | Adriaan Moors <adriaan.moors@epfl.ch> | 2011-11-08 11:36:30 +0000 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@epfl.ch> | 2011-11-08 11:36:30 +0000 |
commit | 5cc3dad991cde9c55c49c451a93a496a16b3afe1 (patch) | |
tree | 4f3404809ada09beb28fe5de19463b098d4026a4 | |
parent | 0362b6af90a14e919d3ee1fe38216830b1828ec9 (diff) | |
download | scala-5cc3dad991cde9c55c49c451a93a496a16b3afe1.tar.gz scala-5cc3dad991cde9c55c49c451a93a496a16b3afe1.tar.bz2 scala-5cc3dad991cde9c55c49c451a93a496a16b3afe1.zip |
smarter bridges to unapplies
wraps the call to a bridged synthetic unapply(Seq) in a defensive
if-test:
if (x.isInstanceOf[expectedType])
real.unapply(x.asInstanceOf[expectedType]) else None
NOTE: the test is WRONG, but it has to be due to #1697/#2337 -- once those are fixed, this one should generate the expected output
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Erasure.scala | 15 | ||||
-rw-r--r-- | test/files/run/patmat_unapp_abstype.check | 2 | ||||
-rw-r--r-- | test/files/run/patmat_unapp_abstype.scala | 39 |
3 files changed, 54 insertions, 2 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index aaae703d06..a2eddd34bd 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -755,7 +755,7 @@ abstract class Erasure extends AddInterfaces while (opc.hasNext) { val member = opc.overriding val other = opc.overridden - //Console.println("bridge? " + member + ":" + member.tpe + member.locationString + " to " + other + ":" + other.tpe + other.locationString)//DEBUG + //println("bridge? " + member + ":" + member.tpe + member.locationString + " to " + other + ":" + other.tpe + other.locationString)//DEBUG if (atPhase(currentRun.explicitouterPhase)(!member.isDeferred)) { val otpe = erasure(owner, other.tpe) val bridgeNeeded = atPhase(phase.next) ( @@ -790,8 +790,19 @@ abstract class Erasure extends AddInterfaces member.tpe match { case MethodType(List(), ConstantType(c)) => Literal(c) case _ => - (((Select(This(owner), member): Tree) /: bridge.paramss) + val bridgingCall = (((Select(This(owner), member): Tree) /: bridge.paramss) ((fun, vparams) => Apply(fun, vparams map Ident))) + // type checking ensures we can safely call `other`, but unless `member.tpe <:< other.tpe`, calling `member` is not guaranteed to succeed + // in general, there's nothing we can do about this, except for an unapply: when this subtype test fails, return None without calling `member` + if ( member.isSynthetic // TODO: should we do this for user-defined unapplies as well? + && ((member.name == nme.unapply) || (member.name == nme.unapplySeq)) + // && (bridge.paramss.nonEmpty && bridge.paramss.head.nonEmpty && bridge.paramss.head.tail.isEmpty) // does the first argument list has exactly one argument -- for user-defined unapplies we can't be sure + && !(atPhase(phase.next)(member.tpe <:< other.tpe))) { // no static guarantees (TODO: is the subtype test ever true?) + import CODE._ + val typeTest = gen.mkIsInstanceOf(REF(bridge.paramss.head.head), member.tpe.params.head.tpe, any = true, wrapInApply = true) // any = true since we're before erasure (?), wrapInapply is true since we're after uncurry + // println("unapp type test: "+ typeTest) + IF (typeTest) THEN bridgingCall ELSE REF(NoneModule) + } else bridgingCall }); debuglog("generating bridge from " + other + "(" + Flags.flagsToString(bridge.flags) + ")" + ":" + otpe + other.locationString + " to " + member + ":" + erasure(owner, member.tpe) + member.locationString + " =\n " + bridgeDef); bridgeDef diff --git a/test/files/run/patmat_unapp_abstype.check b/test/files/run/patmat_unapp_abstype.check new file mode 100644 index 0000000000..ac28ccdb95 --- /dev/null +++ b/test/files/run/patmat_unapp_abstype.check @@ -0,0 +1,2 @@ +TypeRef +none of the above diff --git a/test/files/run/patmat_unapp_abstype.scala b/test/files/run/patmat_unapp_abstype.scala new file mode 100644 index 0000000000..e5adec5c16 --- /dev/null +++ b/test/files/run/patmat_unapp_abstype.scala @@ -0,0 +1,39 @@ +// abstract types and extractors, oh my! +trait TypesAPI { + trait Type + + // an alternative fix (implemented in the virtual pattern matcher, is to replace the isInstanceOf by a manifest-based run-time test) + // that's what typeRefMani is for + type TypeRef <: Type //; implicit def typeRefMani: Manifest[TypeRef] + val TypeRef: TypeRefExtractor; trait TypeRefExtractor { + def apply(x: Int): TypeRef + def unapply(x: TypeRef): Option[(Int)] + } + + // just for illustration, should follow the same pattern as TypeRef + case class MethodType(n: Int) extends Type +} + +// user should not be exposed to the implementation +trait TypesUser extends TypesAPI { + def shouldNotCrash(tp: Type): Unit = { + tp match { + case TypeRef(x) => println("TypeRef") + case MethodType(x) => println("MethodType") + case _ => println("none of the above") + } + } +} + +trait TypesImpl extends TypesAPI { + object TypeRef extends TypeRefExtractor // this will have a bridged unapply(x: Type) = unapply(x.asInstanceOf[TypeRef]) + case class TypeRef(n: Int) extends Type // this has a bridge from TypesAPI#Type to TypesImpl#TypeRef + // --> the cast in the bridge will fail because the pattern matcher can't type test against the abstract types in TypesUser + //lazy val typeRefMani = manifest[TypeRef] +} + +object Test extends TypesImpl with TypesUser with App { + shouldNotCrash(TypeRef(10)) // should and does print "TypeRef" + // once #1697/#2337 are fixed, this should generate the correct output + shouldNotCrash(MethodType(10)) // should print "MethodType" but prints "none of the above" -- good one, pattern matcher! +}
\ No newline at end of file |