@@ -11,6 +11,7 @@ import dotty.tools.dotc.ast.{Trees, tpd}
11
11
import ast .Trees ._
12
12
import dotty .tools .dotc .core .StdNames ._
13
13
import dotty .tools .dotc .core .Flags
14
+ import dotty .tools .dotc .core .Flags .FlagSet
14
15
import dotty .tools .dotc .transform .TreeTransforms .{MiniPhaseTransform , TransformerInfo , TreeTransform }
15
16
import dotty .tools .dotc .util .Positions .Position
16
17
@@ -42,52 +43,38 @@ class PhantomRefErasure extends MiniPhaseTransform with InfoTransformer {
42
43
// Transform trees
43
44
44
45
override def transformStats (trees : List [Tree ])(implicit ctx : Context , info : TransformerInfo ): List [Tree ] = {
45
- ctx.owner match {
46
- case cls : ClassSymbol if cls.classDenot.classSymbol.derivesFrom(defn.PhantomAnyClass ) =>
47
- trees.foreach {
48
- case tree : TypeDef =>
49
- case tree : DefDef =>
50
- case tree =>
51
- ctx.error(s " Phantom classes can not have expressions in statement position. " , tree.pos)
52
- }
53
- case _ =>
54
- trees.foreach {
55
- case tree@ Apply (fun, _) =>
56
- fun match {
57
- case Select (_ : This , name) if name == nme.CONSTRUCTOR =>
58
- case _ if tree.tpe.derivesFrom(defn.PhantomAnyClass ) =>
59
- ctx.error(s " Functions returning a phantom type can not be in statement position. " , fun.pos)
60
- case _ =>
61
- }
62
- case tree@ TypeApply (fun, _) if tree.tpe.derivesFrom(defn.PhantomAnyClass ) =>
63
- ctx.error(s " Functions returning a phantom type can not be in statement position. " , fun.pos)
64
- case tree : Select if tree.tpe.derivesFrom(defn.PhantomAnyClass ) =>
65
- ctx.error(s " Fields containing a phantom type can not be accessed in statement position. " , tree.pos)
66
- case _ =>
67
- }
68
- }
69
- trees
46
+ checkStats(trees)
47
+ filterStats(trees)
70
48
}
71
49
72
- override def transformApply (tree : Apply )(implicit ctx : Context , info : TransformerInfo ): Tree = {
73
- val paramTypes = {
74
- tree.fun.typeOpt match {
75
- case tpe : TermRef =>
76
- tpe.info match {
77
- case tpe2 : MethodType => tpe2.paramTypes
78
- case _ => Nil
79
- }
50
+ override def transformSelect (tree : Select )(implicit ctx : Context , info : TransformerInfo ): Tree = {
51
+ tree.qualifier match {
52
+ case _ : Ident => tree
53
+ case qual if tree. tpe.derivesFrom(defn. PhantomAnyClass ) =>
54
+ // We keep the name of selected member in the name of the synthetic val to ease debugging.
55
+ val synthVal = SyntheticValDef (ctx.freshName( " phantom$ " + tree.name + " $ " ).toTermName, tree.qualifier)
56
+ val synthValRef = Ident (synthVal.symbol.termRef)
57
+ transform( Block ( List (synthVal), Select (synthValRef, tree.name)))
80
58
81
- case tpe : MethodType => tpe.paramTypes
82
- case _ => Nil
83
- }
59
+ case _ => tree
84
60
}
61
+ }
85
62
86
- val newTree =
87
- if (paramTypes.nonEmpty || tree.args.isEmpty) tree
88
- else cpy.Apply (tree)(tree.fun, Nil )
63
+ override def transformApply (tree : Apply )(implicit ctx : Context , info : TransformerInfo ): Tree = {
64
+ @ tailrec def hasParams (tpe : Type ): Boolean = tpe match {
65
+ case tpe : TermRef => hasParams(tpe.info)
66
+ case tpe : MethodType => tpe.paramTypes.nonEmpty
67
+ case _ => false
68
+ }
89
69
90
- super .transformApply(newTree)
70
+ if (tree.args.isEmpty || hasParams(tree.fun.typeOpt)) {
71
+ tree
72
+ } else {
73
+ val args = filterStats(tree.args.map(transform))
74
+ val newApply = cpy.Apply (tree)(tree.fun, Nil )
75
+ if (args.isEmpty) newApply
76
+ else Block (args, newApply)
77
+ }
91
78
}
92
79
93
80
override def transformDefDef (ddef : DefDef )(implicit ctx : Context , info : TransformerInfo ): Tree = {
@@ -116,6 +103,67 @@ class PhantomRefErasure extends MiniPhaseTransform with InfoTransformer {
116
103
super .transformDefDef(ddef1)
117
104
}
118
105
106
+ private def filterStats (trees : List [Tree ])(implicit ctx : Context ): List [Tree ] = {
107
+ val flatStats = trees.flatMap {
108
+ case Block (stats, expr) if expr.tpe.derivesFrom(defn.PhantomAnyClass ) => stats :+ expr
109
+ case tree => List (tree)
110
+ }
111
+ flatStats.filter(! _.tpe.derivesFrom(defn.PhantomAnyClass ))
112
+ }
113
+
114
+ private def checkStats (trees : List [Tree ])(implicit ctx : Context ): Unit = ctx.owner match {
115
+ case cls : ClassSymbol if cls.classDenot.classSymbol.derivesFrom(defn.PhantomAnyClass ) =>
116
+ trees.foreach {
117
+ case tree : TypeDef => // OK
118
+ case tree : ValDef => // OK
119
+ case tree : DefDef => checkDefDef(tree)
120
+ case tree =>
121
+ ctx.error(s " Phantom classes can not have expressions in statement position. " , tree.pos)
122
+ }
123
+
124
+ case _ =>
125
+ def checkTree (tree : Tree , pos : Position ): Unit = tree match {
126
+ case _ : TypeDef => // OK
127
+ case _ : ValDef => // OK
128
+ case tree : DefDef => checkDefDef(tree)
129
+ case Apply (Select (_ : This , name), _) if name == nme.CONSTRUCTOR => // OK
130
+ case _ if tree.tpe.derivesFrom(defn.PhantomAnyClass ) =>
131
+ ctx.error(s " Expression returning a phantom type can not be in statement position. " , pos)
132
+ case _ =>
133
+ }
134
+ trees.foreach(tree => checkTree(tree, tree.pos))
135
+ }
136
+
137
+ private def checkDefDef (ddef : DefDef )(implicit ctx : Context ): Unit = {
138
+ def valOrDefDefDeclKeyword (flags : FlagSet ): String = {
139
+ if (! flags.is(Flags .Accessor )) " def"
140
+ else if (flags.is(Flags .Mutable )) " var"
141
+ else if (flags.is(Flags .Lazy )) " lazy val"
142
+ else " val"
143
+ }
144
+
145
+ def classDeclKeyword (flags : FlagSet ): String = {
146
+ if (flags.is(Flags .Trait )) " trait"
147
+ else if (flags.is(Flags .Abstract )) " abstract class"
148
+ else " class"
149
+ }
150
+
151
+ val flags = ddef.symbol.flags
152
+ if (ddef.tpt.tpe.derivesFrom(defn.PhantomAnyClass )) {
153
+ if (flags.is(Flags .Mutable ) || flags.is(Flags .Lazy ))
154
+ ctx.error(s " Can not define ' ${valOrDefDefDeclKeyword(flags)}' with phantom type. " , ddef.pos)
155
+ } else if (ddef.name != nme.CONSTRUCTOR ) {
156
+ ddef.symbol.owner match {
157
+ case cls : ClassSymbol if cls.classSymbol.derivesFrom(defn.PhantomAnyClass ) =>
158
+ val defKey = valOrDefDefDeclKeyword(flags)
159
+ val classKey = classDeclKeyword(ddef.symbol.owner.flags)
160
+ ctx.error(s " Can not define ' $defKey' with non phantom type in a phantom ' $classKey'. " , ddef.pos)
161
+
162
+ case _ => // OK
163
+ }
164
+ }
165
+ }
166
+
119
167
// Transform symbols
120
168
121
169
def transformInfo (tp : Type , sym : Symbol )(implicit ctx : Context ): Type = erasedPhantoms(tp)
@@ -137,4 +185,5 @@ class PhantomRefErasure extends MiniPhaseTransform with InfoTransformer {
137
185
case _ => MethodType (erasedParamNames, erasedParamTypes, erasedResultType)
138
186
}
139
187
}
188
+
140
189
}
0 commit comments