blob: f33baa52b810bf64b4f0c5607e324c607a8f0078 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
package dotty.tools.dotc
package transform
import TreeTransforms._
import core.DenotTransformers._
import core.Symbols._
import core.Contexts._
import core.Types._
import core.Flags._
import core.Decorators._
import core.StdNames.nme
import ast.Trees._
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Constants._
/** This phase rewrites idempotent expressions with constant types to Literals.
* The constant types are eliminated by erasure, so we need to keep
* the info about constantness in the trees.
*
* The phase also makes sure that the constant of a literal is the same as the constant
* in the type of the literal.
*/
class Literalize extends MiniPhaseTransform { thisTransform =>
import ast.tpd._
override def phaseName: String = "literalize"
/** Note: Demanding idempotency instead of purity is strictly speaking too loose.
* Example
*
* object O { final val x = 42; println("43") }
* O.x
*
* Strictly speaking we can't replace `O.x` with `42`. But this would make
* most expressions non-constant. Maybe we can change the spec to accept this
* kind of eliding behavior. Or else enforce true purity in the compiler.
* The choice will be affected by what we will do with `inline` and with
* Singleton type bounds (see SIP 23). Presumably
*
* object O1 { val x: Singleton = 42; println("43") }
* object O2 { inline val x = 42; println("43") }
*
* should behave differently.
*
* O1.x should have the same effect as { println("43"; 42 }
*
* whereas
*
* O2.x = 42
*
* Revisit this issue once we have implemented `inline`. Then we can demand
* purity of the prefix unless the selection goes to an inline val.
*/
def literalize(tree: Tree)(implicit ctx: Context): Tree = {
def recur(tp: Type): Tree = tp match {
case ConstantType(value) if isIdempotentExpr(tree) => Literal(value)
case tp: TermRef if tp.symbol.isStable => recur(tp.info.widenExpr)
case _ => tree
}
recur(tree.tpe)
}
override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree =
literalize(tree)
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree =
literalize(tree)
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
literalize(tree)
override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree =
literalize(tree)
override def transformLiteral(tree: Literal)(implicit ctx: Context, info: TransformerInfo): Tree = tree.tpe match {
case ConstantType(const) if tree.const.value != const.value || (tree.const.tag != const.tag) => Literal(const)
case _ => tree
}
/** Check that all literals have types match underlying constants
*/
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
tree match {
case Literal(c @ Constant(treeValue)) =>
tree.tpe match {
case ConstantType(c2 @ Constant(typeValue)) =>
assert(treeValue == typeValue && c2.tag == c.tag,
i"Type of Literal $tree is inconsistent with underlying constant")
case tpe =>
assert(c.tpe =:= tpe, i"Type of Literal $tree is inconsistent with underlying constant type ${c.tpe}")
}
case _ =>
}
}
}
|