aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/NameKinds.scala
blob: 0f08e470103edf2d15d9cd19af2711ba50e7b9f3 (plain)
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
package dotty.tools
package dotc
package core

import Names._
import NameOps._
import StdNames._
import util.DotClass
import tasty.TastyFormat._
import Decorators._
import Contexts.Context
import collection.mutable

object NameKinds {

  // These are sharable since all NameKinds are created eagerly at the start of the program
  // before any concurrent threads are forked. for this to work, NameKinds should never
  // be created lazily or in modules that start running after compilers are forked.
  @sharable private val simpleNameKinds = new mutable.HashMap[Int, ClassifiedNameKind]
  @sharable private val qualifiedNameKinds = new mutable.HashMap[Int, QualifiedNameKind]
  @sharable private val uniqueNameKinds = new mutable.HashMap[String, UniqueNameKind]

  abstract class NameInfo extends DotClass {
    def kind: NameKind
    def mkString(underlying: TermName): String
    def map(f: SimpleTermName => SimpleTermName): NameInfo = this
  }

  abstract class NameKind(val tag: Int) extends DotClass { self =>
    type ThisInfo <: Info
    class Info extends NameInfo { this: ThisInfo =>
      def kind = self
      def mkString(underlying: TermName) = self.mkString(underlying, this)
      override def toString = infoString
    }
    def definesNewName = false
    def unmangle(name: SimpleTermName): TermName = name
    def mkString(underlying: TermName, info: ThisInfo): String
    def infoString: String
  }

  object SimpleTermNameKind extends NameKind(UTF8) { self =>
    type ThisInfo = Info
    val info = new Info
    def mkString(underlying: TermName, info: ThisInfo) = unsupported("mkString")
    def infoString = unsupported("infoString")
  }

  abstract class ClassifiedNameKind(tag: Int, val infoString: String) extends NameKind(tag) {
    type ThisInfo = Info
    val info = new Info
    def apply(qual: TermName) =
      qual.derived(info)
    def unapply(name: DerivedTermName): Option[TermName] =  name match {
      case DerivedTermName(underlying, `info`) => Some(underlying)
      case _ => None
    }
    simpleNameKinds(tag) = this
  }

  class PrefixNameKind(tag: Int, prefix: String, optInfoString: String = "")
  extends ClassifiedNameKind(tag, if (optInfoString.isEmpty) s"Prefix $prefix" else optInfoString) {
    def mkString(underlying: TermName, info: ThisInfo) =
      underlying.mapLast(n => termName(prefix + n.toString)).toString
    override def unmangle(name: SimpleTermName): TermName =
      if (name.startsWith(prefix)) apply(name.drop(prefix.length).asSimpleName)
      else name
  }

  class SuffixNameKind(tag: Int, suffix: String, optInfoString: String = "")
  extends ClassifiedNameKind(tag, if (optInfoString.isEmpty) s"Suffix $suffix" else optInfoString) {
    def mkString(underlying: TermName, info: ThisInfo) = underlying.toString ++ suffix
    override def unmangle(name: SimpleTermName): TermName =
      if (name.endsWith(suffix)) apply(name.take(name.length - suffix.length).asSimpleName)
      else name
  }

  trait QualifiedInfo extends NameInfo {
    val name: SimpleTermName
  }

  class QualifiedNameKind(tag: Int, val separator: String)
  extends NameKind(tag) {
    type ThisInfo = QualInfo
    case class QualInfo(val name: SimpleTermName) extends Info with QualifiedInfo {
      override def map(f: SimpleTermName => SimpleTermName): NameInfo = new QualInfo(f(name))
      override def toString = s"$infoString $name"
    }
    def apply(qual: TermName, name: SimpleTermName): TermName =
      qual.derived(new QualInfo(name))

    /** Overloaded version used only for ExpandedName and TraitSetterName.
     *  Needed because the suffix of an expanded name may itself be expanded.
     *  For example, look at javap of scala.App.initCode
     */
    def apply(qual: TermName, name: TermName): TermName = name rewrite {
      case name: SimpleTermName => apply(qual, name)
      case AnyQualifiedName(_, _) => apply(qual, name.toSimpleName)
    }

    def unapply(name: DerivedTermName): Option[(TermName, SimpleTermName)] = name match {
      case DerivedTermName(qual, info: this.QualInfo) => Some((qual, info.name))
      case _ => None
    }

    override def definesNewName = true

    def mkString(underlying: TermName, info: ThisInfo) =
      s"$underlying$separator${info.name}"
    def infoString = s"Qualified $separator"

    qualifiedNameKinds(tag) = this
  }

  object AnyQualifiedName {
    def unapply(name: DerivedTermName): Option[(TermName, SimpleTermName)] = name match {
      case DerivedTermName(qual, info: QualifiedInfo) =>
        Some((name.underlying, info.name))
      case _ => None
    }
  }

  trait NumberedInfo extends NameInfo {
    def num: Int
  }

  abstract class NumberedNameKind(tag: Int, val infoString: String) extends NameKind(tag) { self =>
    type ThisInfo = NumberedInfo
    case class NumberedInfo(val num: Int) extends Info with NameKinds.NumberedInfo {
      override def toString = s"$infoString $num"
    }
    def apply(qual: TermName, num: Int) =
      qual.derived(new NumberedInfo(num))
    def unapply(name: DerivedTermName): Option[(TermName, Int)] = name match {
      case DerivedTermName(underlying, info: this.NumberedInfo) => Some((underlying, info.num))
      case _ => None
    }
    protected def skipSeparatorAndNum(name: SimpleTermName, separator: String): Int = {
      var i = name.length
      while (i > 0 && name(i - 1).isDigit) i -= 1
      if (i > separator.length && i < name.length &&
          name.slice(i - separator.length, i).toString == separator) i
      else -1
    }
  }

  object AnyNumberedName {
    def unapply(name: DerivedTermName): Option[(TermName, Int)] = name match {
      case DerivedTermName(qual, info: NumberedInfo) => Some((qual, info.num))
      case _ => None
    }
  }

  case class UniqueNameKind(val separator: String)
  extends NumberedNameKind(UNIQUE, s"Unique $separator") {
    override def definesNewName = true
    def mkString(underlying: TermName, info: ThisInfo) = {
      val safePrefix = str.sanitize(underlying.toString + separator)
      safePrefix + info.num
    }

    def fresh(prefix: TermName = EmptyTermName)(implicit ctx: Context): TermName =
      ctx.freshNames.newName(prefix, this)

    uniqueNameKinds(separator) = this
  }

  object AnyUniqueName {
    def unapply(name: DerivedTermName): Option[(TermName, String, Int)] = name match {
      case DerivedTermName(qual, info: NumberedInfo) =>
        info.kind match {
          case unique: UniqueNameKind => Some((qual, unique.separator, info.num))
          case _ => None
        }
      case _ => None
    }
  }

  val QualifiedName           = new QualifiedNameKind(QUALIFIED, ".")
  val FlatName                = new QualifiedNameKind(FLATTENED, "$")
  val ExpandPrefixName        = new QualifiedNameKind(EXPANDPREFIX, "$")

  val ExpandedName = new QualifiedNameKind(EXPANDED, str.EXPAND_SEPARATOR) {
    private val FalseSuper = termName("$$super")
    private val FalseSuperLength = FalseSuper.length

    override def unmangle(name: SimpleTermName): TermName = {
      var i = name.lastIndexOfSlice(str.EXPAND_SEPARATOR)
      if (i < 0) name
      else {
        // Hack to make super accessors from traits work. They would otherwise fail because of #765
        // The problem is that in `x$$super$$plus` the expansion prefix needs to be `x`
        // instead of `x$$super`.
        if (i > FalseSuperLength && name.slice(i - FalseSuperLength, i) == FalseSuper)
          i -= FalseSuper.length

        apply(name.take(i).asTermName, name.drop(i + str.EXPAND_SEPARATOR.length).asSimpleName)
      }
    }
  }

  val TraitSetterName         = new QualifiedNameKind(TRAITSETTER, str.TRAIT_SETTER_SEPARATOR)

  val UniqueName = new UniqueNameKind("$") {
    override def mkString(underlying: TermName, info: ThisInfo) =
      if (underlying.isEmpty) "$" + info.num + "$" else super.mkString(underlying, info)
  }

  val InlineAccessorName      = new UniqueNameKind("$_inlineAccessor_$")
  val TempResultName          = new UniqueNameKind("ev$")
  val EvidenceParamName       = new UniqueNameKind("evidence$")
  val DepParamName            = new UniqueNameKind("<param>")
  val LazyImplicitName        = new UniqueNameKind("$_lazy_implicit_$")
  val LazyLocalName           = new UniqueNameKind("$lzy")
  val LazyLocalInitName       = new UniqueNameKind("$lzyINIT")
  val LazyFieldOffsetName     = new UniqueNameKind("$OFFSET")
  val LazyBitMapName          = new UniqueNameKind(nme.BITMAP_PREFIX.toString)
  val NonLocalReturnKeyName   = new UniqueNameKind("nonLocalReturnKey")
  val WildcardParamName       = new UniqueNameKind("_$")
  val TailLabelName           = new UniqueNameKind("tailLabel")
  val ExceptionBinderName     = new UniqueNameKind("ex")
  val SkolemName              = new UniqueNameKind("?")
  val LiftedTreeName          = new UniqueNameKind("liftedTree")

  val UniqueExtMethName = new UniqueNameKind("$extension") {
    override def unmangle(name: SimpleTermName): TermName = {
      val i = skipSeparatorAndNum(name, separator)
      if (i > 0) {
        val index = name.drop(i).toString.toInt
        var original = name.take(i - separator.length).asTermName
        apply(original, index)
      }
      else name
    }
  }

  val PatMatStdBinderName     = new UniqueNameKind("x")
  val PatMatPiName            = new UniqueNameKind("pi") // FIXME: explain what this is
  val PatMatPName             = new UniqueNameKind("p")  // FIXME: explain what this is
  val PatMatOName             = new UniqueNameKind("o")  // FIXME: explain what this is
  val PatMatCaseName          = new UniqueNameKind("case")
  val PatMatMatchFailName     = new UniqueNameKind("matchFail")
  val PatMatSelectorName      = new UniqueNameKind("selector")

  object DefaultGetterName extends NumberedNameKind(DEFAULTGETTER, "DefaultGetter") {
    def mkString(underlying: TermName, info: ThisInfo) = {
      val prefix = if (underlying.isConstructorName) nme.DEFAULT_GETTER_INIT else underlying
      prefix.toString + str.DEFAULT_GETTER + (info.num + 1)
    }
    // TODO: Reduce code duplication with UniqueExtMethName
    override def unmangle(name: SimpleTermName): TermName = {
      val i = skipSeparatorAndNum(name, str.DEFAULT_GETTER)
      if (i > 0) {
        val index = name.drop(i).toString.toInt - 1
        var original = name.take(i - str.DEFAULT_GETTER.length).asTermName
        if (original == nme.DEFAULT_GETTER_INIT) original = Names.CONSTRUCTOR
        apply(original, index)
      }
      else name
    }
  }

  object VariantName extends NumberedNameKind(VARIANT, "Variant") {
    val varianceToPrefix = Map(-1 -> '-', 0 -> '=', 1 -> '+')
    def mkString(underlying: TermName, info: ThisInfo) = {
      varianceToPrefix(info.num).toString + underlying
    }
  }

  /** Names of the form N_<outer>. Emitted by inliner, replaced by outer path
   *  in ExplicitOuter.
   */
  object OuterSelectName extends NumberedNameKind(OUTERSELECT, "OuterSelect") {
    def mkString(underlying: TermName, info: ThisInfo) = {
      assert(underlying.isEmpty)
      info.num + "_<outer>"
    }
  }

  val SuperAccessorName = new PrefixNameKind(SUPERACCESSOR, "super$")
  val InitializerName = new PrefixNameKind(INITIALIZER, "initial$")
  val ShadowedName = new PrefixNameKind(SHADOWED, "(shadowed)")
  val ProtectedAccessorName = new PrefixNameKind(PROTECTEDACCESSOR, "protected$")
  val ProtectedSetterName = new PrefixNameKind(PROTECTEDSETTER, "protected$set") // dubious encoding, kept for Scala2 compatibility
  val AvoidClashName = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$")
  val DirectName = new SuffixNameKind(DIRECT, "$direct")
  val FieldName = new SuffixNameKind(FIELD, "$$local")
  val ExtMethName = new SuffixNameKind(EXTMETH, "$extension")
  val ModuleVarName = new SuffixNameKind(OBJECTVAR, "$module")
  val ModuleClassName = new SuffixNameKind(OBJECTCLASS, "$", optInfoString = "ModuleClass")

  object SignedName extends NameKind(63) {

    /** @param parts  resultSig followed by paramsSig */
    case class SignedInfo(sig: Signature) extends Info {
      override def toString = s"$infoString $sig"
    }
    type ThisInfo = SignedInfo

    def apply(qual: TermName, sig: Signature) =
      qual.derived(new SignedInfo(sig))
    def unapply(name: DerivedTermName): Option[(TermName, Signature)] = name match {
      case DerivedTermName(underlying, info: SignedInfo) => Some((underlying, info.sig))
      case _ => None
    }

    def mkString(underlying: TermName, info: ThisInfo): String = unsupported("mkString")
    def infoString: String = "Signed"
  }

  val Scala2MethodNameKinds: List[NameKind] =
    List(DefaultGetterName, ExtMethName, UniqueExtMethName, ProtectedAccessorName, ProtectedSetterName)

  def simpleNameKindOfTag      : collection.Map[Int, ClassifiedNameKind] = simpleNameKinds
  def qualifiedNameKindOfTag   : collection.Map[Int, QualifiedNameKind]  = qualifiedNameKinds
  def uniqueNameKindOfSeparator: collection.Map[String, UniqueNameKind]  = uniqueNameKinds
}