Skip to content

Commit 8ade045

Browse files
committed
WIP: do not erase full arguments.
1 parent f692f1c commit 8ade045

File tree

3 files changed

+98
-76
lines changed

3 files changed

+98
-76
lines changed

src/dotty/tools/dotc/transform/PhantomDeclErasure.scala

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -54,42 +54,9 @@ class PhantomDeclErasure extends MiniPhaseTransform with InfoTransformer {
5454

5555
override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = {
5656
val newTrees = trees.filter {
57-
case ValDef(_, tpt, _) =>
58-
!tpt.tpe.derivesFrom(defn.PhantomAnyClass)
59-
60-
case tree @ DefDef(name, _, _, tpt, _) =>
61-
val flags = tree.symbol.flags
62-
val isPhantom = tpt.tpe.derivesFrom(defn.PhantomAnyClass)
63-
if (isPhantom) {
64-
if (flags.is(Flags.Mutable))
65-
ctx.error("Can not define 'var' with phantom type.", tree.pos)
66-
else if (flags.is(Flags.Lazy))
67-
ctx.error("Can not define 'lazy val' with phantom type.", tree.pos)
68-
} else if (name != nme.CONSTRUCTOR) {
69-
tree.symbol.owner match {
70-
case cls: ClassSymbol if cls.classSymbol.derivesFrom(defn.PhantomAnyClass) =>
71-
val defKey =
72-
if (!flags.is(Flags.Accessor)) "def"
73-
else if (flags.is(Flags.Mutable)) "var"
74-
else if (flags.is(Flags.Lazy)) "lazy val"
75-
else "val"
76-
val flags2 = tree.symbol.owner.flags
77-
val classKey =
78-
if (flags2.is(Flags.Trait)) "trait"
79-
else if (flags2.is(Flags.Abstract)) "abstract class"
80-
else "class"
81-
ctx.error(s"Can not define '$defKey' with non phantom type in a phantom '$classKey'.", tree.pos)
82-
83-
case _ =>
84-
}
85-
}
86-
!isPhantom
87-
88-
case tree @ TypeDef(_, _) =>
89-
!tree.tpe.derivesFrom(defn.PhantomAnyClass)
90-
91-
case _ =>
92-
true
57+
case tree: ValOrDefDef => !tree.tpt.tpe.derivesFrom(defn.PhantomAnyClass)
58+
case tree: TypeDef => !tree.tpe.derivesFrom(defn.PhantomAnyClass)
59+
case _ => true
9360
}
9461
super.transformStats(newTrees)
9562
}

src/dotty/tools/dotc/transform/PhantomRefErasure.scala

Lines changed: 89 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import dotty.tools.dotc.ast.{Trees, tpd}
1111
import ast.Trees._
1212
import dotty.tools.dotc.core.StdNames._
1313
import dotty.tools.dotc.core.Flags
14+
import dotty.tools.dotc.core.Flags.FlagSet
1415
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo, TreeTransform}
1516
import dotty.tools.dotc.util.Positions.Position
1617

@@ -42,52 +43,38 @@ class PhantomRefErasure extends MiniPhaseTransform with InfoTransformer {
4243
// Transform trees
4344

4445
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)
7048
}
7149

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)))
8058

81-
case tpe: MethodType => tpe.paramTypes
82-
case _ => Nil
83-
}
59+
case _ => tree
8460
}
61+
}
8562

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+
}
8969

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+
}
9178
}
9279

9380
override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
@@ -116,6 +103,67 @@ class PhantomRefErasure extends MiniPhaseTransform with InfoTransformer {
116103
super.transformDefDef(ddef1)
117104
}
118105

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+
119167
// Transform symbols
120168

121169
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = erasedPhantoms(tp)
@@ -137,4 +185,5 @@ class PhantomRefErasure extends MiniPhaseTransform with InfoTransformer {
137185
case _ => MethodType(erasedParamNames, erasedParamTypes, erasedResultType)
138186
}
139187
}
188+
140189
}

tests/run/phantom.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,23 @@ fun5
1111
fun5
1212
fun5
1313
polyfun1
14+
Boo6
1415
polyfun2
16+
Boo6
1517
polyfun2
1618
polyfun2
1719
polyfun2
1820
polyfun2
21+
Boo6
1922
polyfun3
23+
Boo6
2024
polyfun3
2125
polyfun3
2226
polyfun3
2327
polyfun3
28+
Boo6
2429
polyfun4
30+
Boo6
2531
polyfun4
2632
polyfun4
2733
polyfun4

0 commit comments

Comments
 (0)