summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2013-06-26 11:15:08 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2013-06-26 11:15:08 -0700
commitdeb88538116ab0f1fe598c91e4d026a83ecc5404 (patch)
tree61253772d79c93c7aa7142e325ea3e0654d162b1 /src
parentd22e8d282c538a711f6fdd33793c06cd7676959d (diff)
parent228549356ce5dda0305dc2e9e08870859e4de06e (diff)
downloadscala-deb88538116ab0f1fe598c91e4d026a83ecc5404.tar.gz
scala-deb88538116ab0f1fe598c91e4d026a83ecc5404.tar.bz2
scala-deb88538116ab0f1fe598c91e4d026a83ecc5404.zip
Merge pull request #2655 from VladUreche/issue/7344
SI-7344 Specialize methods in private scopes
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala118
1 files changed, 94 insertions, 24 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index 5ef5d4031b..7e85647592 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -844,7 +844,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
debuglog("%s expands to %s in %s".format(sym, specMember.name.decode, pp(env)))
info(specMember) = NormalizedMember(sym)
newOverload(sym, specMember, env)
- owner.info.decls.enter(specMember)
+ // if this is a class, we insert the normalized member in scope,
+ // if this is a method, there's no attached scope for it (EmptyScope)
+ val decls = owner.info.decls
+ if (decls != EmptyScope)
+ decls.enter(specMember)
specMember
}
}
@@ -1420,7 +1424,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
val env = unify(symbol.tpe, tree.tpe, emptyEnv, false)
debuglog("[specSym] checking for rerouting: %s with \n\tsym.tpe: %s, \n\ttree.tpe: %s \n\tenv: %s \n\tname: %s"
.format(tree, symbol.tpe, tree.tpe, env, specializedName(symbol, env)))
- if (!env.isEmpty) { // a method?
+ if (env.nonEmpty) { // a method?
val specCandidates = qual.tpe.member(specializedName(symbol, env))
val specMember = specCandidates suchThat { s =>
doesConform(symbol, tree.tpe, qual.tpe.memberType(s), env)
@@ -1438,6 +1442,34 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
} else None
}
+ /** Computes residual type parameters after rewiring, like "String" in the following example:
+ * ```
+ * def specMe[@specialized T, U](t: T, u: U) = ???
+ * specMe[Int, String](1, "2") => specMe$mIc$sp[String](1, "2")
+ * ```
+ */
+ def computeResidualTypeVars(baseTree: Tree, specTree: Tree, baseTargs: List[Tree], env: TypeEnv) = {
+ val baseSym: Symbol = baseTree.symbol
+ val specSym: Symbol = specTree.symbol
+ val residualTargs = baseSym.info.typeParams zip baseTargs collect {
+ case (tvar, targ) if !env.contains(tvar) || !isPrimitiveValueClass(env(tvar).typeSymbol) => targ
+ }
+
+ if (specSym.info.typeParams.isEmpty && residualTargs.nonEmpty) {
+ log("!!! Type args to be applied, but symbol says no parameters: " + ((specSym.defString, residualTargs)))
+ baseTree
+ }
+ else {
+ ifDebug(assert(residualTargs.length == specSym.info.typeParams.length,
+ "residual: %s, tparams: %s, env: %s".format(residualTargs, specSym.info.typeParams, env))
+ )
+
+ val tree1 = gen.mkTypeApply(specTree, residualTargs)
+ debuglog("rewrote " + tree + " to " + tree1)
+ localTyper.typedOperator(atPos(tree.pos)(tree1))
+ }
+ }
+
curTree = tree
tree match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
@@ -1470,43 +1502,44 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
transformSuperApply
+ // This rewires calls to specialized methods defined in a class (which have a receiver)
+ // class C {
+ // def foo[@specialized T](t: T): T = t
+ // C.this.foo(3) // TypeApply(Select(This(C), foo), List(Int)) => C.this.foo$mIc$sp(3)
+ // }
case TypeApply(sel @ Select(qual, name), targs)
- if (!specializedTypeVars(symbol.info).isEmpty && name != nme.CONSTRUCTOR) =>
- def transformTypeApply = {
+ if (specializedTypeVars(symbol.info).nonEmpty && name != nme.CONSTRUCTOR) =>
debuglog("checking typeapp for rerouting: " + tree + " with sym.tpe: " + symbol.tpe + " tree.tpe: " + tree.tpe)
val qual1 = transform(qual)
- // log(">>> TypeApply: " + tree + ", qual1: " + qual1)
+ log(">>> TypeApply: " + tree + ", qual1: " + qual1)
specSym(qual1) match {
case Some(specMember) =>
debuglog("found " + specMember.fullName)
ifDebug(assert(symbol.info.typeParams.length == targs.length, symbol.info.typeParams + " / " + targs))
val env = typeEnv(specMember)
- val residualTargs = symbol.info.typeParams zip targs collect {
- case (tvar, targ) if !env.contains(tvar) || !isPrimitiveValueClass(env(tvar).typeSymbol) => targ
- }
- // See SI-5583. Don't know why it happens now if it didn't before.
- if (specMember.info.typeParams.isEmpty && residualTargs.nonEmpty) {
- log("!!! Type args to be applied, but symbol says no parameters: " + ((specMember.defString, residualTargs)))
- localTyper.typed(sel)
- }
- else {
- ifDebug(assert(residualTargs.length == specMember.info.typeParams.length,
- "residual: %s, tparams: %s, env: %s".format(residualTargs, specMember.info.typeParams, env))
- )
-
- val tree1 = gen.mkTypeApply(Select(qual1, specMember), residualTargs)
- debuglog("rewrote " + tree + " to " + tree1)
- localTyper.typedOperator(atPos(tree.pos)(tree1)) // being polymorphic, it must be a method
- }
+ computeResidualTypeVars(tree, gen.mkAttributedSelect(qual1, specMember), targs, env)
case None =>
treeCopy.TypeApply(tree, treeCopy.Select(sel, qual1, name), super.transformTrees(targs))
// See pos/exponential-spec.scala - can't call transform on the whole tree again.
// super.transform(tree)
}
+
+ // This rewires calls to specialized methods defined in the local scope. For example:
+ // def outerMethod = {
+ // def foo[@specialized T](t: T): T = t
+ // foo(3) // TypeApply(Ident(foo), List(Int)) => foo$mIc$sp(3)
+ // }
+ case TypeApply(sel @ Ident(name), targs) if name != nme.CONSTRUCTOR =>
+ val env = unify(symbol.tpe, tree.tpe, emptyEnv, false)
+ if (env.isEmpty) super.transform(tree)
+ else {
+ overloads(symbol) find (_ matchesEnv env) match {
+ case Some(Overload(specMember, _)) => computeResidualTypeVars(tree, Ident(specMember), targs, env)
+ case _ => super.transform(tree)
+ }
}
- transformTypeApply
case Select(Super(_, _), _) if illegalSpecializedInheritance(currentClass) =>
val pos = tree.pos
@@ -1657,7 +1690,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
localTyper.typed(deriveDefDef(tree)(rhs => rhs))
}
}
- transformDefDef
+ expandInnerNormalizedMembers(transformDefDef)
+
+ case ddef @ DefDef(_, _, _, _, _, _) =>
+ val tree1 = expandInnerNormalizedMembers(tree)
+ super.transform(tree1)
case ValDef(_, _, _, _) if symbol.hasFlag(SPECIALIZED) && !symbol.isParamAccessor =>
def transformValDef = {
@@ -1682,6 +1719,39 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}
+ /**
+ * This performs method specialization inside a scope other than a {class, trait, object}: could be another method
+ * or a value. This specialization is much simpler, since there is no need to record the new members in the class
+ * signature, their signatures are only visible locally. It works according to the usual logic:
+ * - we use normalizeMember to create the specialized symbols
+ * - we leave DefDef stubs in the tree that are later filled in by tree duplication and adaptation
+ * @see duplicateBody
+ */
+ private def expandInnerNormalizedMembers(tree: Tree) = tree match {
+ case ddef @ DefDef(_, _, _, vparams :: Nil, _, rhs)
+ if ddef.symbol.owner.isMethod &&
+ specializedTypeVars(ddef.symbol.info).nonEmpty &&
+ !ddef.symbol.hasFlag(SPECIALIZED) =>
+
+ val sym = ddef.symbol
+ val owner = sym.owner
+ val norm = normalizeMember(owner, sym, emptyEnv)
+
+ if (norm.length > 1) {
+ // record the body for duplication
+ body(sym) = rhs
+ parameters(sym) = vparams.map(_.symbol)
+ // to avoid revisiting the member, we can set the SPECIALIZED
+ // flag. nobody has to see this anyway :)
+ sym.setFlag(SPECIALIZED)
+ // create empty bodies for specializations
+ localTyper.typed(Block(norm.tail.map(sym => DefDef(sym, { vparamss => EmptyTree })), ddef))
+ } else
+ tree
+ case _ =>
+ tree
+ }
+
/** Duplicate the body of the given method `tree` to the new symbol `source`.
*
* Knowing that the method can be invoked only in the `castmap` type environment,