Skip to content

Commit 5118fd0

Browse files
committed
New phase that creates extension methods for inline classes.
1 parent 98d7f7e commit 5118fd0

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/* NSC -- new Scala compiler
2+
* Copyright 2005-2011 LAMP/EPFL
3+
* @author Gilles Dubochet
4+
* @author Martin Odersky
5+
*/
6+
7+
package scala.tools.nsc
8+
package transform
9+
10+
import symtab._
11+
import Flags._
12+
import scala.collection.{ mutable, immutable }
13+
import scala.collection.mutable
14+
import scala.tools.nsc.util.FreshNameCreator
15+
import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple }
16+
import sun.tools.tree.OrExpression
17+
18+
/**
19+
* Perform Step 1 in the inline classes SIP
20+
*
21+
* @author Martin Odersky
22+
* @version 2.10
23+
*/
24+
abstract class ClassInlining extends Transform with TypingTransformers {
25+
26+
import global._ // the global environment
27+
import definitions._ // standard classes and methods
28+
import typer.{ typed, atOwner } // methods to type trees
29+
30+
/** the following two members override abstract members in Transform */
31+
val phaseName: String = "inlineclasses"
32+
33+
def newTransformer(unit: CompilationUnit): Transformer =
34+
new ClassInliner(unit)
35+
36+
def hasUnboxedVersion(sym: Symbol) =
37+
!sym.isParamAccessor && !sym.isConstructor
38+
39+
/** Generate stream of possible names for the unboxed version of given instance method `imeth`.
40+
* If the method is not overloaded, this stream consists of just "unboxed$imeth".
41+
* If the method is overloaded, the stream has as first element "unboxedX$imeth", where X is the
42+
* index of imeth in the sequence of overloaded alternatives with the same name. This choice will
43+
* always be picked as the name of the generated unboxed method.
44+
* After this first choice, all other possible indices in the range of 0 until the number
45+
* of overloaded alternatives are returned. The secondary choices are used to find a matching method
46+
* in `unboxedMethod` if the first name has the wrong type. We thereby gain a level of insensitivity
47+
* of how overloaded types are ordered between phases and picklings.
48+
*/
49+
private def unboxedNames(imeth: Symbol): Stream[Name] =
50+
imeth.owner.info.decl(imeth.name).tpe match {
51+
case OverloadedType(_, alts) =>
52+
val index = alts indexOf imeth
53+
assert(index >= 0, alts+" does not contain "+imeth)
54+
def altName(index: Int) = newTermName("unboxed"+index+"$"+imeth.name)
55+
altName(index) #:: ((0 until alts.length).toStream filter (index !=) map altName)
56+
case tpe =>
57+
assert(tpe != NoType, imeth.name+" not found in "+imeth.owner+"'s decls: "+imeth.owner.info.decls)
58+
Stream(newTermName("unboxed$"+imeth.name))
59+
}
60+
61+
/** Return the unboxed method that corresponds to given instance method `meth`.
62+
*/
63+
def unboxedMethod(imeth: Symbol): Symbol = atPhase(currentRun.refchecksPhase) {
64+
val companionInfo = imeth.owner.companionModule.info
65+
val candidates = unboxedNames(imeth) map (companionInfo.decl(_))
66+
val matching = candidates filter (alt => normalize(alt.tpe, imeth.owner) matches imeth.tpe)
67+
assert(matching.nonEmpty, "no unboxed method found for "+imeth+" among "+candidates+"/"+unboxedNames(imeth))
68+
matching.head
69+
}
70+
71+
private def normalize(stpe: Type, clazz: Symbol): Type = stpe match {
72+
case PolyType(tparams, restpe) =>
73+
GenPolyType(tparams dropRight clazz.typeParams.length, normalize(restpe, clazz))
74+
case MethodType(tparams, restpe) =>
75+
restpe
76+
case _ =>
77+
stpe
78+
}
79+
80+
class ClassInliner(unit: CompilationUnit) extends TypingTransformer(unit) {
81+
82+
private val unboxedDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]()
83+
84+
def unboxedMethInfo(unboxedMeth: Symbol, origInfo: Type, clazz: Symbol): Type = {
85+
var newTypeParams = cloneSymbolsAtOwner(clazz.typeParams, unboxedMeth)
86+
val thisParamType = appliedType(clazz.typeConstructor, newTypeParams map (_.tpe))
87+
val thisParam = unboxedMeth.newValueParameter(nme.SELF, unboxedMeth.pos) setInfo thisParamType
88+
def transform(clonedType: Type): Type = clonedType match {
89+
case MethodType(params, restpe) =>
90+
MethodType(List(thisParam), clonedType)
91+
case NullaryMethodType(restpe) =>
92+
MethodType(List(thisParam), restpe)
93+
}
94+
val GenPolyType(tparams, restpe) = origInfo cloneInfo unboxedMeth
95+
GenPolyType(tparams ::: newTypeParams, transform(restpe))
96+
}
97+
98+
private def allParams(tpe: Type): List[Symbol] = tpe match {
99+
case MethodType(params, res) => params ::: allParams(res)
100+
case _ => List()
101+
}
102+
103+
override def transform(tree: Tree): Tree = {
104+
tree match {
105+
case Template(_, _, _) =>
106+
if (currentOwner.isInlineClass) {
107+
unboxedDefs(currentOwner.companionModule) = new mutable.ListBuffer[Tree]
108+
super.transform(tree)
109+
}
110+
else tree
111+
case DefDef(mods, name, tparams, vparamss, tpt, rhs)
112+
if currentOwner.isInlineClass && hasUnboxedVersion(tree.symbol) =>
113+
val companion = currentOwner.companionModule
114+
val origMeth = tree.symbol
115+
val unboxedName = unboxedNames(origMeth).head
116+
val unboxedMeth = companion.moduleClass.newMethod(unboxedName, origMeth.pos, origMeth.flags & ~OVERRIDE | FINAL)
117+
.setAnnotations(origMeth.annotations)
118+
companion.info.decls.enter(unboxedMeth)
119+
unboxedMeth.setInfo(unboxedMethInfo(unboxedMeth, origMeth.info, currentOwner))
120+
def thisParamRef = gen.mkAttributedIdent(unboxedMeth.info.params.head setPos unboxedMeth.pos)
121+
val GenPolyType(unboxedTpeParams, unboxedMono) = unboxedMeth.info
122+
val origTpeParams = origMeth.typeParams ::: currentOwner.typeParams
123+
val unboxedBody = rhs
124+
.substTreeSyms(origTpeParams, unboxedTpeParams)
125+
.substTreeSyms(vparamss.flatten map (_.symbol), allParams(unboxedMono).tail)
126+
.substTreeThis(currentOwner, thisParamRef)
127+
.changeOwner((origMeth, unboxedMeth))
128+
unboxedDefs(companion) += atPos(tree.pos) { DefDef(unboxedMeth, unboxedBody) }
129+
val unboxedCallPrefix = Apply(
130+
gen.mkTypeApply(gen.mkAttributedRef(companion), unboxedMeth, origTpeParams map (_.tpe)),
131+
List(This(currentOwner)))
132+
val unboxedCall = atOwner(origMeth) {
133+
localTyper.typed {
134+
atPos(rhs.pos) {
135+
(unboxedCallPrefix /: vparamss) {
136+
case (fn, params) => Apply(fn, params map (param => Ident(param.symbol)))
137+
}
138+
}
139+
}
140+
}
141+
treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, unboxedCall)
142+
case _ =>
143+
super.transform(tree)
144+
}
145+
}
146+
147+
override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] =
148+
super.transformStats(stats, exprOwner) map {
149+
case stat @ ModuleDef(mods, name, tmpl @ Template(parents, self, body)) =>
150+
unboxedDefs.remove(stat.symbol) match {
151+
case Some(buf) =>
152+
val unboxedDefs = buf.toList map { mdef => atOwner(stat.symbol) { localTyper.typed(mdef) }}
153+
treeCopy.ModuleDef(stat, mods, name, treeCopy.Template(tmpl, parents, self, body ++ buf))
154+
case None =>
155+
stat
156+
}
157+
case stat =>
158+
stat
159+
}
160+
}
161+
}

0 commit comments

Comments
 (0)