summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Iry <jamesiry@gmail.com>2013-01-29 16:58:54 -0800
committerJames Iry <jamesiry@gmail.com>2013-01-29 16:58:54 -0800
commit7026376dcc87f531de84c99aa3e52068f5b10874 (patch)
treeeeb9dbcb2553b5771cd337eb53cd5d97aa17aa35
parentc1dd8bbaa4cc688ab05fc325a02e20b91488a583 (diff)
parentcfaa3b5408eb3e6eabe108d3adcb06fdcafe912a (diff)
downloadscala-7026376dcc87f531de84c99aa3e52068f5b10874.tar.gz
scala-7026376dcc87f531de84c99aa3e52068f5b10874.tar.bz2
scala-7026376dcc87f531de84c99aa3e52068f5b10874.zip
Merge pull request #1912 from retronym/ticket/6651
SI-6651 Extension methods types may depend on the typed of the wrapped value
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala37
-rw-r--r--test/files/pos/t6651.scala33
2 files changed, 63 insertions, 7 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
index 39e16c3f58..589aa43ac2 100644
--- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
@@ -75,7 +75,19 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
val candidates = extensionNames(imeth) map (companionInfo.decl(_)) filter (_.exists)
val matching = candidates filter (alt => normalize(alt.tpe, imeth.owner) matches imeth.tpe)
assert(matching.nonEmpty,
- s"no extension method found for $imeth:${imeth.tpe} among ${candidates.map(c => c.name+":"+c.tpe).toList} / ${extensionNames(imeth).toList}")
+ sm"""|no extension method found for:
+ |
+ | $imeth:${imeth.tpe}
+ |
+ | Candidates:
+ |
+ | ${candidates.map(c => c.name+":"+c.tpe).mkString("\n")}
+ |
+ | Candidates (signatures normalized):
+ |
+ | ${candidates.map(c => c.name+":"+normalize(c.tpe, imeth.owner)).mkString("\n")}
+ |
+ | Eligible Names: ${extensionNames(imeth).mkString(",")}"""")
matching.head
}
@@ -94,11 +106,18 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
*/
private def normalize(stpe: Type, clazz: Symbol): Type = stpe match {
case PolyType(tparams, restpe) =>
- GenPolyType(tparams dropRight clazz.typeParams.length, normalize(restpe.substSym(tparams takeRight clazz.typeParams.length, clazz.typeParams), clazz))
+ // Split the type parameters of the extension method into two groups,
+ // corresponding the to class and method type parameters.
+ val numClassParams = clazz.typeParams.length
+ val methTParams = tparams dropRight numClassParams
+ val classTParams = tparams takeRight numClassParams
+
+ GenPolyType(methTParams,
+ normalize(restpe.substSym(classTParams, clazz.typeParams), clazz))
case MethodType(List(thiz), restpe) if thiz.name == nme.SELF =>
- restpe
- case MethodType(tparams, restpe) =>
- MethodType(tparams.drop(1), restpe)
+ restpe.substituteTypes(thiz :: Nil, clazz.thisType :: Nil)
+ case MethodType(thiz :: params, restpe) =>
+ MethodType(params, restpe)
case _ =>
stpe
}
@@ -128,7 +147,11 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
MethodType(List(thisParam), restpe)
}
val GenPolyType(tparams, restpe) = origInfo cloneInfo extensionMeth
- GenPolyType(tparams ::: newTypeParams, transform(restpe) substSym (clazz.typeParams, newTypeParams))
+ val selfParamSingletonType = singleType(currentOwner.companionModule.thisType, thisParam)
+ GenPolyType(
+ tparams ::: newTypeParams,
+ transform(restpe) substThisAndSym (clazz, selfParamSingletonType, clazz.typeParams, newTypeParams)
+ )
}
private def allParams(tpe: Type): List[Symbol] = tpe match {
@@ -162,7 +185,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
origMeth.defString,
extensionMeth.defString)) // extensionMeth.defStringSeenAs(origInfo
- def thisParamRef = gen.mkAttributedIdent(extensionMeth.info.params.head setPos extensionMeth.pos)
+ def thisParamRef = gen.mkAttributedStableRef(extensionMeth.info.params.head setPos extensionMeth.pos)
val GenPolyType(extensionTpeParams, extensionMono) = extensionMeth.info
val origTpeParams = (tparams map (_.symbol)) ::: currentOwner.typeParams
val extensionBody = rhs
diff --git a/test/files/pos/t6651.scala b/test/files/pos/t6651.scala
new file mode 100644
index 0000000000..55a3b74e4c
--- /dev/null
+++ b/test/files/pos/t6651.scala
@@ -0,0 +1,33 @@
+class YouAreYourself[A <: AnyRef](val you: A) extends AnyVal {
+ def yourself: you.type = you
+}
+
+object Test {
+ val s = ""
+ val s1: s.type = new YouAreYourself[s.type](s).yourself
+}
+
+trait Path {
+ type Dep <: AnyRef
+}
+
+final class ValueClass[P <: Path](val path: P) extends AnyVal {
+ import path.Dep
+
+ def apply(dep: Dep)(d2: dep.type, foo: Int): (Dep, d2.type) = (d2, d2)
+
+ // This generates dodgy code; note `ValueClass.this`:
+ //
+ // final def bounds$extension[D >: Nothing <: ValueClass.this.path.Dep,
+ // P >: Nothing <: Path]
+ // ($this: ValueClass[P])
+ // (dep: D)
+ // (d2: dep.type, foo: Int): (D, d2.type) = scala.Tuple2.apply[D, d2.type](d2, d2);
+ //
+ // Nothing crashes down the line, but it certainly doesn't conform to best-practices.
+ //
+ // An better alternative would be to add a type parameter for the (singleton) type of
+ // the wrapped value.
+ def bounds[D <: Dep](dep: D)(d2: dep.type, foo: Int): (D, d2.type) = (d2, d2)
+}
+