1
+ package dotty .tools .dotc
2
+ package transform
3
+
4
+ import core .Phases ._
5
+ import core .DenotTransformers ._
6
+ import core .Denotations ._
7
+ import core .SymDenotations ._
8
+ import core .Symbols ._
9
+ import core .Contexts ._
10
+ import core .Types ._
11
+ import core .Names ._
12
+ import core .StdNames ._
13
+ import core .NameOps ._
14
+ import core .Decorators ._
15
+ import core .Constants ._
16
+ import typer .NoChecking
17
+ import typer .ProtoTypes ._
18
+ import typer .ErrorReporting ._
19
+ import core .transform .Erasure ._
20
+ import core .Decorators ._
21
+ import ast .{tpd , untpd }
22
+ import ast .Trees ._
23
+
24
+ class Erasure extends Phase with DenotTransformer {
25
+
26
+ override def name : String = " erasure"
27
+
28
+ def transform (ref : SingleDenotation )(implicit ctx : Context ): SingleDenotation = ref match {
29
+ case ref : SymDenotation =>
30
+ assert(ctx.phase == this , s " transforming $ref at ${ctx.phase}" )
31
+ ref.copySymDenotation(info = transformInfo(ref.symbol, ref.info))
32
+ case ref =>
33
+ ref.derivedSingleDenotation(ref.symbol, erasure(ref.info))
34
+ }
35
+
36
+ val eraser = new Erasure .Typer
37
+
38
+ def run (implicit ctx : Context ): Unit = {
39
+ val unit = ctx.compilationUnit
40
+ unit.tpdTree = eraser.typedExpr(unit.tpdTree)(ctx.fresh.withPhase(this .next))
41
+ }
42
+ }
43
+
44
+ object Erasure {
45
+
46
+ import tpd ._
47
+
48
+ object Boxing {
49
+
50
+ def isUnbox (sym : Symbol )(implicit ctx : Context ) =
51
+ sym.name == nme.unbox && (defn.ScalaBoxedClasses contains sym.owner)
52
+
53
+ def isBox (sym : Symbol )(implicit ctx : Context ) =
54
+ sym.name == nme.box && (defn.ScalaValueClasses contains sym.owner)
55
+
56
+ def boxMethod (cls : ClassSymbol )(implicit ctx : Context ) = cls.info.member(nme.box).symbol
57
+ def unboxMethod (cls : ClassSymbol )(implicit ctx : Context ) = cls.info.member(nme.unbox).symbol
58
+
59
+ /** Isf this tree is an unbox operation which can be safely removed
60
+ * when enclosed in a box, the unboxed argument, otherwise EmptyTree.
61
+ * Note that one can't always remove a Box(Unbox(x)) combination because the
62
+ * process of unboxing x may lead to throwing an exception.
63
+ * This is important for specialization: calls to the super constructor should not box/unbox specialized
64
+ * fields (see TupleX). (ID)
65
+ */
66
+ private def safelyRemovableUnboxArg (tree : Tree )(implicit ctx : Context ): Tree = tree match {
67
+ case Apply (fn, arg :: Nil )
68
+ if isUnbox(fn.symbol) && (defn.ScalaBoxedClasses contains arg.tpe.typeSymbol) =>
69
+ arg
70
+ case _ =>
71
+ EmptyTree
72
+ }
73
+
74
+ def isErasedValueType (tpe : Type )(implicit ctx : Context ): Boolean = tpe.isInstanceOf [ErasedValueType ]
75
+ def isPrimitiveValueType (tpe : Type )(implicit ctx : Context ): Boolean = tpe.classSymbol.isPrimitiveValueClass
76
+
77
+ def constant (tree : Tree , const : Tree )(implicit ctx : Context ) =
78
+ if (isIdempotentExpr(tree)) Block (tree :: Nil , const) else const
79
+
80
+ final def box (tree : Tree , target : => String = " " )(implicit ctx : Context ): Tree = ctx.traceIndented(i " boxing ${tree.showSummary}: ${tree.tpe} into $target" ) {
81
+ tree.tpe match {
82
+ case ErasedValueType (clazz, _) =>
83
+ New (clazz.typeRef, cast(tree, clazz.underlyingOfValueClass) :: Nil ) // todo: use adaptToType?
84
+ case _ =>
85
+ val cls = tree.tpe.classSymbol
86
+ if (cls eq defn.UnitClass ) constant(tree, ref(defn.BoxedUnit_UNIT ))
87
+ else if (cls eq defn.NothingClass ) tree // a non-terminating expression doesn't need boxing
88
+ else {
89
+ assert(cls ne defn.ArrayClass )
90
+ val arg = safelyRemovableUnboxArg(tree)
91
+ if (arg.isEmpty) Apply (ref(boxMethod(cls.asClass)), tree :: Nil )
92
+ else {
93
+ ctx.log(s " boxing an unbox: ${tree.symbol} -> ${arg.tpe}" )
94
+ arg
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ def unbox (tree : Tree , pt : Type )(implicit ctx : Context ): Tree = ctx.traceIndented(i " unboxing ${tree.showSummary}: ${tree.tpe} as a $pt" ) {
101
+ pt match {
102
+ case ErasedValueType (clazz, underlying) =>
103
+ val tree1 =
104
+ if ((tree.tpe isRef defn.NullClass ) && isPrimitiveValueType(underlying))
105
+ // convert `null` directly to underlying type, as going
106
+ // via the unboxed type would yield a NPE (see SI-5866)
107
+ unbox(tree, underlying)
108
+ else
109
+ Apply (Select (adaptToType(tree, clazz.typeRef), clazz.valueClassUnbox), Nil )
110
+ cast(tree1, pt)
111
+ case _ =>
112
+ val cls = pt.classSymbol
113
+ if (cls eq defn.UnitClass ) constant(tree, Literal (Constant (())))
114
+ else {
115
+ assert(cls ne defn.ArrayClass )
116
+ Apply (ref(unboxMethod(cls.asClass)), tree :: Nil )
117
+ }
118
+ }
119
+ }
120
+
121
+ /** Generate a synthetic cast operation from tree.tpe to pt.
122
+ */
123
+ def cast (tree : Tree , pt : Type )(implicit ctx : Context ): Tree =
124
+ if (pt isRef defn.UnitClass ) unbox(tree, pt)
125
+ else (tree.tpe, pt) match {
126
+ case (defn.ArrayType (treeElem), defn.ArrayType (ptElem))
127
+ if isPrimitiveValueType(treeElem) && ! isPrimitiveValueType(ptElem) =>
128
+ // See SI-2386 for one example of when this might be necessary.
129
+ cast(runtimeCall(nme.toObjectArray, tree :: Nil ), pt)
130
+ case _ =>
131
+ println(s " casting from ${tree.showSummary}: ${tree.tpe.show} to ${pt.show}" )
132
+ TypeApply (Select (tree, defn.Object_asInstanceOf ), TypeTree (pt) :: Nil )
133
+ }
134
+
135
+ /** Adaptation of an expression `e` to an expected type `PT`, applying the following
136
+ * rewritings exhaustively as long as the type of `e` is not a subtype of `PT`.
137
+ *
138
+ * e -> box(e) if `e` is of erased value type
139
+ * e -> unbox(e, PT) otherwise, if `PT` is an erased value type
140
+ * e -> box(e) if `e` is of primitive type and `PT` is not a primitive type
141
+ * e -> unbox(e, PT) if `PT` is a primitive type and `e` is not of primitive type
142
+ * e -> cast(e, PT) otherwise
143
+ */
144
+ def adaptToType (tree : Tree , pt : Type )(implicit ctx : Context ): Tree =
145
+ if (tree.tpe <:< pt)
146
+ tree
147
+ else if (isErasedValueType(tree.tpe))
148
+ adaptToType(box(tree), pt)
149
+ else if (isErasedValueType(pt))
150
+ adaptToType(unbox(tree, pt), pt)
151
+ else if (isPrimitiveValueType(tree.tpe) && ! isPrimitiveValueType(pt))
152
+ adaptToType(box(tree), pt)
153
+ else if (isPrimitiveValueType(pt) && ! isPrimitiveValueType(tree.tpe))
154
+ adaptToType(unbox(tree, pt), pt)
155
+ else
156
+ cast(tree, pt)
157
+ }
158
+
159
+ class Typer extends typer.Typer with NoChecking {
160
+ import Boxing ._
161
+
162
+ def box (tree : Tree ): Tree = ???
163
+ def unbox (tree : Tree , target : Type ): Tree = ???
164
+ def cast (tree : Tree , target : Type ): Tree = ???
165
+
166
+ private def promote (tree : untpd.Tree )(implicit ctx : Context ): tree.ThisTree [Type ] = {
167
+ assert(tree.hasType)
168
+ println(s " prompting ${tree.show}: ${tree.tpe.asInstanceOf [Type ].showWithUnderlying(2 )}" )
169
+ tree.withType(erasure(tree.tpe.asInstanceOf [Type ]))
170
+ }
171
+
172
+ override def typedIdent (tree : untpd.Ident , pt : Type )(implicit ctx : Context ): Tree = {
173
+ val tree1 = promote(tree)
174
+ println(i " typed ident ${tree.name}: ${tree1.tpe} at phase ${ctx.phase}, history = ${tree1.symbol.history}" )
175
+ tree1
176
+ }
177
+
178
+ /** Type check select nodes, applying the following rewritings exhaustively
179
+ * on selections `e.m`.
180
+ *
181
+ * e.m1 -> e.m2 if `m1` is a member of Any or AnyVal and `m2` is
182
+ * the same-named member in Object.
183
+ * e.m -> box(e).m if `e` is primitive and `m` is a member or a reference class
184
+ * or `e` has an erased value class type.
185
+ * e.m -> unbox(e).m if `e` is not primitive and `m` is a member of a primtive type.
186
+ *
187
+ * Additionally, if the type of `e` does not derive from the type `OT` of the owner of `m`,
188
+ * the following rewritings are performed, where `ET` is the erased type of the selection's
189
+ * original qualifier expression.
190
+ *
191
+ * e.m -> cast(OT).m if `m` is not an array operation
192
+ * e.m -> cast(ET).m if `m` is an array operation and `ET` is an array type
193
+ * e.m -> runtime.array_m(e)
194
+ * if `m` is an array operation and `ET` is Object
195
+ */
196
+ override def typedSelect (tree : untpd.Select , pt : Type )(implicit ctx : Context ): Tree = {
197
+ val sym = tree.symbol
198
+ assert(sym.exists)
199
+
200
+ def select (qual : Tree , sym : Symbol ): Tree =
201
+ untpd.cpy.Select (tree, qual, sym.name) withType qual.tpe.select(sym)
202
+
203
+ def selectArrayMember (qual : Tree , erasedPre : Type ) =
204
+ if (erasedPre isRef defn.ObjectClass ) runtimeCall(tree.name.genericArrayOp, qual :: Nil )
205
+ else recur(cast(qual, erasedPre))
206
+
207
+ def recur (qual : Tree ): Tree = {
208
+ val qualIsPrimitive = isPrimitiveValueType(qual.tpe)
209
+ val symIsPrimitive = sym.owner.isPrimitiveValueClass
210
+ if ((sym.owner eq defn.AnyClass ) || (sym.owner eq defn.AnyValClass ))
211
+ select(qual, defn.ObjectClass .info.decl(sym.name).symbol)
212
+ else if (qualIsPrimitive && ! symIsPrimitive || isErasedValueType(qual.tpe))
213
+ recur(box(qual))
214
+ else if (! qualIsPrimitive && symIsPrimitive)
215
+ recur(unbox(qual, sym.owner.typeRef))
216
+ else if (qual.tpe.derivesFrom(sym.owner) || qual.isInstanceOf [Super ])
217
+ select(qual, sym)
218
+ else if (sym.owner eq defn.ArrayClass )
219
+ selectArrayMember(qual, erasure(tree.qualifier.tpe))
220
+ else
221
+ recur(cast(qual, sym.owner.typeRef))
222
+ }
223
+
224
+ recur(typed(tree.qualifier, AnySelectionProto ))
225
+ }
226
+
227
+ override def typedTypeApply (tree : untpd.TypeApply , pt : Type )(implicit ctx : Context ) =
228
+ typedExpr(tree.fun, pt)
229
+
230
+ override def typedApply (tree : untpd.Apply , pt : Type )(implicit ctx : Context ): Tree = {
231
+ val Apply (fun, args) = tree
232
+ val fun1 = typedExpr(fun, WildcardType )
233
+ fun1.tpe.widen match {
234
+ case mt : MethodType =>
235
+ val args1 = args.zipWithConserve(mt.paramTypes)(typedExpr)
236
+ untpd.cpy.Apply (tree, fun1, args1) withType mt.resultType
237
+ }
238
+ }
239
+
240
+ override def typedTypeTree (tree : untpd.TypeTree , pt : Type )(implicit ctx : Context ): TypeTree =
241
+ promote(tree)
242
+
243
+ override def ensureNoLocalRefs (block : Block , pt : Type , forcedDefined : Boolean = false )(implicit ctx : Context ): Tree =
244
+ block // optimization, no checking needed, as block symbols do not change.
245
+
246
+ override def typedDefDef (ddef : untpd.DefDef , sym : Symbol )(implicit ctx : Context ) = {
247
+ val ddef1 = untpd.cpy.DefDef (ddef, ddef.mods, ddef.name, Nil , ddef.vparamss, ddef.tpt, ddef.rhs)
248
+ super .typedDefDef(ddef1, sym)
249
+ }
250
+
251
+ override def typedClassDef (cdef : untpd.TypeDef , sym : ClassSymbol )(implicit ctx : Context ) = {
252
+ val TypeDef (mods, name, impl @ Template (constr, parents, self, body)) = cdef
253
+ val cdef1 = untpd.cpy.TypeDef (cdef, mods, name,
254
+ untpd.cpy.Template (impl, constr, parents, untpd.EmptyValDef , body))
255
+ super .typedClassDef(cdef1, sym)
256
+ }
257
+
258
+ /*
259
+ override def transformStats(stats: List[Tree], exprOwner: Symbol)(implicit ctx: Context) = {
260
+ val stats1 = super.transform(stats, exprOwner)
261
+ if (ctx.owner.isClass) addBridges(stats1) else stats1
262
+ }
263
+ */
264
+ override def typedNamed (tree : untpd.NameTree , pt : Type )(implicit ctx : Context ): Tree = {
265
+ if (tree eq untpd.EmptyValDef ) return tpd.EmptyValDef
266
+ assert(tree.hasType, tree)
267
+ val sym = tree.symbol
268
+ assert(sym.exists, tree)
269
+ def localContext = ctx.fresh.withTree(tree).withOwner(sym)
270
+ tree match {
271
+ case tree : untpd.Ident => typedIdent(tree, pt)
272
+ case tree : untpd.Select => typedSelect(tree, pt)
273
+ case tree : untpd.ValDef => typedValDef(tree, sym)(localContext)
274
+ case tree : untpd.DefDef => typedDefDef(tree, sym)(localContext)
275
+ case tree : untpd.TypeDef =>
276
+ if (tree.isClassDef) typedClassDef(tree, sym.asClass)(localContext)
277
+ else EmptyTree
278
+ }
279
+ }
280
+
281
+ override def adapt (tree : Tree , pt : Type )(implicit ctx : Context ): Tree =
282
+ ctx.traceIndented(i " adapting ${tree.showSummary}: ${tree.tpe} to $pt" , show = true ) {
283
+ assert(ctx.phase == ctx.erasurePhase.next)
284
+ if (tree.isEmpty) tree else adaptToType(tree, pt)
285
+ }
286
+ }
287
+ }
0 commit comments