summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-02-01 17:32:11 -0800
committerPaul Phillips <paulp@improving.org>2013-02-01 17:38:14 -0800
commit08aed64c0b88aecfb88e8e8024582b278d29f39a (patch)
tree20f2335ffd22d7d84ff3b858f4b23d49030e603e
parent5b4a6bf82be1444bd9c5616eaff45dc3a59f22c8 (diff)
parentccd7abe897ab23056e6bf66f02c279647dfe3a57 (diff)
downloadscala-08aed64c0b88aecfb88e8e8024582b278d29f39a.tar.gz
scala-08aed64c0b88aecfb88e8e8024582b278d29f39a.tar.bz2
scala-08aed64c0b88aecfb88e8e8024582b278d29f39a.zip
Merge commit 'ccd7abe897' into wip/fresh-merge2
Conflicts: src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala168
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala2
-rw-r--r--test/files/pos/t6516.scala19
-rw-r--r--test/files/pos/t6651.scala33
4 files changed, 139 insertions, 83 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
index 12933af138..f929b1c48e 100644
--- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
@@ -71,7 +71,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
}
@@ -95,21 +107,36 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
/** This method removes the `$this` argument from the parameter list a method.
*
* A method may be a `PolyType`, in which case we tear out the `$this` and the class
- * type params from its nested `MethodType`. Or it may be a MethodType, as
- * described at the ExtensionMethodType extractor.
+ * type params from its nested `MethodType`.
+ * It may be a `MethodType`, either with a curried parameter list in which the first argument
+ * is a `$this` - we just return the rest of the list.
+ * This means that the corresponding symbol was generated during `extmethods`.
+ *
+ * It may also be a `MethodType` in which the `$this` does not appear in a curried parameter list.
+ * The curried lists disappear during `uncurry`, and the methods may be duplicated afterwards,
+ * for instance, during `specialize`.
+ * In this case, the first argument is `$this` and we just get rid of it.
*/
private def normalize(stpe: Type, clazz: Symbol): Type = stpe match {
case PolyType(tparams, restpe) =>
- // method type parameters, class type parameters
- val (mtparams, ctparams) = tparams splitAt (tparams.length - clazz.typeParams.length)
- GenPolyType(mtparams, normalize(restpe.substSym(ctparams, clazz.typeParams), clazz))
- case ExtensionMethodType(etpe) =>
- etpe
+ // 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.substituteTypes(thiz :: Nil, clazz.thisType :: Nil)
+ case MethodType(thiz :: params, restpe) =>
+ MethodType(params, restpe)
case _ =>
stpe
}
class Extender(unit: CompilationUnit) extends TypingTransformer(unit) {
+
private val extensionDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]()
def checkNonCyclic(pos: Position, seen: Set[Symbol], clazz: Symbol): Unit =
@@ -137,34 +164,30 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
* some higher level facilities.
*/
def extensionMethInfo(extensionMeth: Symbol, origInfo: Type, clazz: Symbol): Type = {
- val GenPolyType(tparamsFromMethod, methodResult) = origInfo cloneInfo extensionMeth
- // Start with the class type parameters - clones will be method type parameters
- // so must drop their variance.
- val tparamsFromClass = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth) map (_ resetFlag COVARIANT | CONTRAVARIANT)
- def fix(tp: Type) = tp.substSym(clazz.typeParams, tparamsFromClass)
-
- val thisParamType = appliedType(clazz, tparamsFromClass map (_.tpeHK): _*)
+ // No variance for method type parameters
+ var newTypeParams = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth) map (_ resetFlag COVARIANT | CONTRAVARIANT)
+ val thisParamType = appliedType(clazz.typeConstructor, newTypeParams map (_.tpeHK))
val thisParam = extensionMeth.newValueParameter(nme.SELF, extensionMeth.pos) setInfo thisParamType
- val resultType = MethodType(List(thisParam), dropNullaryMethod(methodResult))
-
- // We can't substitute symbols on the entire polytype because we
- // need to modify the bounds of the cloned type parameters, but we
- // don't want to substitute for the cloned type parameters themselves.
- val tparams = tparamsFromMethod ::: tparamsFromClass
- GenPolyType(tparams map (_ modifyInfo fix), fix(resultType))
-
- // For reference, calling fix on the GenPolyType plays out like this:
- // error: scala.reflect.internal.Types$TypeError: type arguments [B#7344,A#6966]
- // do not conform to method extension$baz#16148's type parameter bounds
- //
- // And the difference is visible here. See how B is bounded from below by A#16149
- // in both cases, but in the failing case, the other type parameter has turned into
- // a different A. (What is that A? It is a clone of the original A created in
- // SubstMap during the call to substSym, but I am not clear on all the particulars.)
- //
- // bad: [B#16154 >: A#16149, A#16155 <: AnyRef#2189]($this#16156: Foo#6965[A#16155])(x#16157: B#16154)List#2457[B#16154]
- // good: [B#16151 >: A#16149, A#16149 <: AnyRef#2189]($this#16150: Foo#6965[A#16149])(x#16153: B#16151)List#2457[B#16151]
+ def transform(clonedType: Type): Type = clonedType match {
+ case MethodType(params, restpe) =>
+ // I assume it was a bug that this was dropping params... [Martin]: No, it wasn't; it's curried.
+ MethodType(List(thisParam), clonedType)
+ case NullaryMethodType(restpe) =>
+ MethodType(List(thisParam), restpe)
+ }
+ val GenPolyType(tparams, restpe) = origInfo cloneInfo extensionMeth
+ 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 {
+ case MethodType(params, res) => params ::: allParams(res)
+ case _ => List()
+ }
+
override def transform(tree: Tree): Tree = {
tree match {
case Template(_, _, _) =>
@@ -179,56 +202,37 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
super.transform(tree)
} else tree
case DefDef(_, _, tparams, vparamss, _, rhs) if tree.symbol.isMethodWithExtension =>
- val origMeth = tree.symbol
- val origThis = currentOwner
- val origTpeParams = tparams.map(_.symbol) ::: origThis.typeParams // method type params ++ class type params
- val origParams = vparamss.flatten map (_.symbol)
- val companion = origThis.companionModule
-
- def makeExtensionMethodSymbol = {
- val extensionName = extensionNames(origMeth).head
- val extensionMeth = (
- companion.moduleClass.newMethod(extensionName.toTermName, origMeth.pos, origMeth.flags & ~OVERRIDE & ~PROTECTED | FINAL)
- setAnnotations origMeth.annotations
- )
- companion.info.decls.enter(extensionMeth)
- }
-
- val extensionMeth = makeExtensionMethodSymbol
- val newInfo = extensionMethInfo(extensionMeth, origMeth.info, origThis)
+ val companion = currentOwner.companionModule
+ val origMeth = tree.symbol
+ val extensionName = extensionNames(origMeth).head
+ val extensionMeth = companion.moduleClass.newMethod(extensionName, origMeth.pos, origMeth.flags & ~OVERRIDE & ~PROTECTED | FINAL)
+ .setAnnotations(origMeth.annotations)
+ companion.info.decls.enter(extensionMeth)
+ val newInfo = extensionMethInfo(extensionMeth, origMeth.info, currentOwner)
extensionMeth setInfo newInfo
-
- log(s"Value class $origThis spawns extension method.\n Old: ${origMeth.defString}\n New: ${extensionMeth.defString}")
-
- val GenPolyType(extensionTpeParams, MethodType(thiz :: Nil, extensionMono)) = newInfo
- val extensionParams = allParameters(extensionMono)
- val extensionThis = gen.mkAttributedIdent(thiz setPos extensionMeth.pos)
-
- val extensionBody = (
- rhs
+ log("Value class %s spawns extension method.\n Old: %s\n New: %s".format(
+ currentOwner,
+ origMeth.defString,
+ extensionMeth.defString)) // extensionMeth.defStringSeenAs(origInfo
+
+ 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
.substituteSymbols(origTpeParams, extensionTpeParams)
- .substituteSymbols(origParams, extensionParams)
- .substituteThis(origThis, extensionThis)
- .changeOwner(origMeth -> extensionMeth)
- )
-
- // Record the extension method ( FIXME: because... ? )
- extensionDefs(companion) += atPos(tree.pos)(DefDef(extensionMeth, extensionBody))
-
- // These three lines are assembling Foo.bar$extension[T1, T2, ...]($this)
- // which leaves the actual argument application for extensionCall.
- val sel = Select(gen.mkAttributedRef(companion), extensionMeth)
- val targs = origTpeParams map (_.tpeHK)
- val callPrefix = gen.mkMethodCall(sel, targs, This(origThis) :: Nil)
-
- // Apply all the argument lists.
- deriveDefDef(tree)(_ =>
- atOwner(origMeth)(
- localTyper.typedPos(rhs.pos)(
- gen.mkForwarder(callPrefix, mmap(vparamss)(_.symbol))
- )
- )
- )
+ .substituteSymbols(vparamss.flatten map (_.symbol), allParams(extensionMono).tail)
+ .substituteThis(currentOwner, thisParamRef)
+ .changeOwner((origMeth, extensionMeth))
+ extensionDefs(companion) += atPos(tree.pos) { DefDef(extensionMeth, extensionBody) }
+ val extensionCallPrefix = Apply(
+ gen.mkTypeApply(gen.mkAttributedRef(companion), extensionMeth, origTpeParams map (_.tpeHK)),
+ List(This(currentOwner)))
+ val extensionCall = atOwner(origMeth) {
+ localTyper.typedPos(rhs.pos) {
+ gen.mkForwarder(extensionCallPrefix, mmap(vparamss)(_.symbol))
+ }
+ }
+ deriveDefDef(tree)(_ => extensionCall)
case _ =>
super.transform(tree)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 7d6d47b410..fb8d6b934f 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -450,7 +450,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam)
if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam)
val aparamtpe = aparam.tpe.dealias match {
- case RefinedType(List(tpe), Scope(sym)) if tpe == MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe
+ case RefinedType(List(tpe), Scope(sym)) if tpe =:= MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe
case tpe => tpe
}
checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam)
diff --git a/test/files/pos/t6516.scala b/test/files/pos/t6516.scala
new file mode 100644
index 0000000000..c004055de2
--- /dev/null
+++ b/test/files/pos/t6516.scala
@@ -0,0 +1,19 @@
+import scala.language.experimental.macros
+import scala.reflect.macros.Context
+import scala.collection.TraversableLike
+
+// This one compiles
+object Test {
+ type Alias[T, CC[_]] = Context { type PrefixType = TraversableLike[T, CC[T]] }
+ def f() = macro f_impl
+ def f_impl(c: Alias[Int, List])() = ???
+}
+
+// This one doesn't
+object Test2 {
+ type Ctx = scala.reflect.macros.Context
+ type Alias[T, CC[_]] = Ctx { type PrefixType = TraversableLike[T, CC[T]] }
+
+ def f() = macro f_impl
+ def f_impl(c: Alias[Int, List])() = ???
+}
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)
+}
+