aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/Contexts.scala
blob: 0898321e591c2fc257d866d05ba8dc442b26d34e (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
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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
package dotty.tools
package dotc
package core

import Decorators._
import Periods._
import Names._
import Phases._
import Types._
import Symbols._
import Scopes._
import NameOps._
import SymDenotations._
import util.Positions._
import ast.Trees._
import ast.untpd
import util.{FreshNameCreator, SimpleMap, SourceFile, NoSource}
import typer._
import Implicits.ContextualImplicits
import config.Settings._
import reporting._
import collection.mutable
import collection.immutable.BitSet
import printing._
import config.{Settings, ScalaSettings, Platform, JavaPlatform}
import language.implicitConversions

object Contexts {

  /** A context is passed basically everywhere in dotc.
   *  This is convenient but carries the risk of captured contexts in
   *  objects that turn into space leaks. To combat this risk, here are some
   *  conventions to follow:
   *
   *    - Never let an implicit context be an argument of a class whose instances
   *      live longer than the context.
   *    - Classes that need contexts for their initialization take an explicit parameter
   *      named `initctx`. They pass initctx to all positions where it is needed
   *      (and these positions should all be part of the intialization sequence of the class).
   *    - Classes that need contexts that survive initialization are instead passed
   *      a "condensed context", typically named `cctx` (or they create one). Consensed contexts
   *      just add some basic information to the context base without the
   *      risk of capturing complete trees.
   *    - To make sure these rules are kept, it would be good to do a sanity
   *      check using bytecode inspection with javap or scalap: Keep track
   *      of all class fields of type context; allow them only in whitelisted
   *      classes (which should be short-lived).
   */
  abstract class Context extends Periods
                            with Substituters
                            with TypeOps
                            with Phases
                            with Printers
                            with Symbols
                            with SymDenotations
                            with Reporting
                            with NamerContextOps
                            with Cloneable { thiscontext =>
    implicit def ctx: Context = this

    /** The context base at the root */
    val base: ContextBase

    /** All outer contexts, ending in `base.initialCtx` and then `NoContext` */
    def outersIterator = new Iterator[Context] {
      var current = thiscontext
      def hasNext = current != NoContext
      def next = { val c = current; current = current.outer; c }
    }

    /** The outer context */
    private[this] var _outer: Context = _
    protected def outer_=(outer: Context) = _outer = outer
    def outer: Context = _outer

    /** The current context */
    private[this] var _period: Period = _
    protected def period_=(period: Period) = _period = period
    def period: Period = _period

    /** The scope nesting level */
    private[this] var _mode: Mode = _
    protected def mode_=(mode: Mode) = _mode = mode
    def mode: Mode = _mode

    /** The current type comparer */
    private[this] var _typerState: TyperState = _
    protected def typerState_=(typerState: TyperState) = _typerState = typerState
    def typerState: TyperState = _typerState

    /** The current plain printer */
    private[this] var _printerFn: Context => Printer = _
    protected def printerFn_=(printerFn: Context => Printer) = _printerFn = printerFn
    def printerFn: Context => Printer = _printerFn

    /** The current owner symbol */
    private[this] var _owner: Symbol = _
    protected def owner_=(owner: Symbol) = _owner = owner
    def owner: Symbol = _owner

    /** The current settings values */
    private[this] var _sstate: SettingsState = _
    protected def sstate_=(sstate: SettingsState) = _sstate = sstate
    def sstate: SettingsState = _sstate

    /** The current tree */
    private[this] var _compilationUnit: CompilationUnit = _
    protected def compilationUnit_=(compilationUnit: CompilationUnit) = _compilationUnit = compilationUnit
    def compilationUnit: CompilationUnit = _compilationUnit

    /** The current tree */
    private[this] var _tree: Tree[_ >: Untyped] = _
    protected def tree_=(tree: Tree[_ >: Untyped]) = _tree = tree
    def tree: Tree[_ >: Untyped] = _tree

    /** The current scope */
    private[this] var _scope: Scope = _
    protected def scope_=(scope: Scope) = _scope = scope
    def scope: Scope = _scope

    /** The current typer */
    private[this] var _typer: Typer = _
    protected def typer_=(typer: Typer) = _typer = typer
    def typer: Typer = _typer

    /** The currently active import info */
    private[this] var _importInfo: ImportInfo = _
    protected def importInfo_=(importInfo: ImportInfo) = _importInfo = importInfo
    def importInfo: ImportInfo = _importInfo

    /** The current compiler-run specific Info */
    private[this] var _runInfo: RunInfo = _
    protected def runInfo_=(runInfo: RunInfo) = _runInfo = runInfo
    def runInfo: RunInfo = _runInfo

    /** An optional diagostics buffer than is used by some checking code
     *  to provide more information in the buffer if it exists.
     */
    private var _diagnostics: Option[StringBuilder] = _
    protected def diagnostics_=(diagnostics: Option[StringBuilder]) = _diagnostics = diagnostics
    def diagnostics: Option[StringBuilder] = _diagnostics

    /** A map in which more contextual properties can be stored */
    private var _moreProperties: Map[String, Any] = _
    protected def moreProperties_=(moreProperties: Map[String, Any]) = _moreProperties = moreProperties
    def moreProperties: Map[String, Any] = _moreProperties

    private var _typeComparer: TypeComparer = _
    protected def typeComparer_=(typeComparer: TypeComparer) = _typeComparer = typeComparer
    def typeComparer: TypeComparer = {
      if (_typeComparer.ctx ne this)
        _typeComparer = _typeComparer.copyIn(this)
      _typeComparer
    }

    /** The new implicit references that are introduced by this scope */
    private var implicitsCache: ContextualImplicits = null
    def implicits: ContextualImplicits = {
      if (implicitsCache == null )
        implicitsCache = {
          val implicitRefs: Set[TermRef] =
            if (isClassDefContext) owner.thisType.implicitMembers
            else if (isImportContext) importInfo.importedImplicits
            else if (isNonEmptyScopeContext) scope.implicitDecls
            else Set()
          if (implicitRefs.isEmpty) outer.implicits
          else new ContextualImplicits(implicitRefs, outer.implicits.ctx)(this)
        }
      implicitsCache
    }

  /** If -Ydebug is on, the top of the stack trace where this context
     *  was created, otherwise `null`.
     */
    private var creationTrace: Array[StackTraceElement] = _

    setCreationTrace()

    private def setCreationTrace() =
      if (true || this.settings.debug.value)
        creationTrace = (new Throwable).getStackTrace().take(20)

    /** Print all enclosing context's creation stacktraces */
    def printCreationTraces() = {
      println("=== context creation trace =======")
      for (ctx <- outersIterator) {
        println(s">>>>>>>>> $ctx")
        if (ctx.creationTrace != null) println(ctx.creationTrace.mkString("\n"))
      }
      println("=== end context creation trace ===")
    }

    /** The current reporter */
    def reporter: Reporter = typerState.reporter

    /** Is this a context for the members of a class definition? */
    def isClassDefContext: Boolean =
      owner.isClass && (owner ne outer.owner)

    /** Is this a context that introduces an import clause? */
    def isImportContext: Boolean =
      (this ne NoContext) && (this.importInfo ne outer.importInfo)

    /** Is this a context that introduces a non-empty scope? */
    def isNonEmptyScopeContext: Boolean =
      (this.scope ne outer.scope) && this.scope.nonEmpty

    /** Leave message in diagnostics buffer if it exists */
    def diagnose(str: => String) =
      for (sb <- diagnostics) {
        sb.setLength(0)
        sb.append(str)
      }

    /** The next outer context whose tree is a template or package definition */
    def enclTemplate: Context = {
      var c = this
      while (c != NoContext && !c.tree.isInstanceOf[Template[_]] && !c.tree.isInstanceOf[PackageDef[_]])
        c = c.outer
      c
    }

    /** The current source file; will be derived from current
     *  compilation unit.
     */
    def source: SourceFile =
      if (compilationUnit == null) NoSource else compilationUnit.source

    /** Does current phase use an erased types interpretation? */
    def erasedTypes: Boolean = phase.erasedTypes

    /** Is the debug option set? */
    def debug: Boolean = base.settings.debug.value

    /** Is the verbose option set? */
    def verbose: Boolean = base.settings.verbose.value

    /** A condensed context containing essential information of this but
     *  no outer contexts except the initial context.
     */
    private var _condensed: CondensedContext = null
    def condensed: CondensedContext = {
      if (_condensed eq outer.condensed)
        _condensed = base.initialCtx.fresh
          .withPeriod(period)
          .withMode(mode)
          // typerState and its constraint is not preserved in condensed
          // reporter is always ThrowingReporter
          .withPrinterFn(printerFn)
          .withOwner(owner)
          .withSettings(sstate)
          // tree is not preserved in condensed
          .withRunInfo(runInfo)
          .withDiagnostics(diagnostics)
          .withMoreProperties(moreProperties)
      _condensed
    }

    /** A fresh clone of this context. */
    def fresh: FreshContext = {
      val newctx = super.clone.asInstanceOf[FreshContext]
      newctx.outer = this
      newctx.implicitsCache = null
      newctx.setCreationTrace()
      newctx
    }

    def withMode(mode: Mode): Context =
      if (mode != this.mode) fresh.withMode(mode) else this

    def addMode(mode: Mode): Context = withMode(this.mode | mode)
    def maskMode(mode: Mode): Context = withMode(this.mode & mode)
    def retractMode(mode: Mode): Context = withMode(this.mode &~ mode)
  }

  /** A condensed context provides only a small memory footprint over
   *  a Context base, and therefore can be stored without problems in
   *  long-lived objects.
   */
  abstract class CondensedContext extends Context {
    override def condensed = this
  }

  /** A fresh context allows selective modification
   *  of its attributes using the with... methods.
   */
  abstract class FreshContext extends CondensedContext {
    def withPeriod(period: Period): this.type = { this.period = period; this }
    override def withMode(mode: Mode): this.type = { this.mode = mode; this }
    def withTyperState(typerState: TyperState): this.type = { this.typerState = typerState; this }
    def withNewTyperState: this.type = withTyperState(typerState.fresh)
    def withPrinterFn(printer: Context => Printer): this.type = { this.printerFn = printer; this }
    def withOwner(owner: Symbol): this.type = { this.owner = owner; this }
    def withSettings(sstate: SettingsState): this.type = { this.sstate = sstate; this }
    def withCompilationUnit(compilationUnit: CompilationUnit): this.type = { this.compilationUnit = compilationUnit; this }
    def withTree(tree: Tree[_ >: Untyped]): this.type = { this.tree = tree; this }
    def withScope(scope: Scope): this.type = { this.scope = scope; this }
    def withNewScope: this.type = { this.scope = newScope; this }
    def withTyper(typer: Typer): this.type = { this.typer = typer; this.scope = typer.scope; this }
    def withImportInfo(importInfo: ImportInfo): this.type = { this.importInfo = importInfo; this }
    def withRunInfo(runInfo: RunInfo): this.type = { this.runInfo = runInfo; this }
    def withDiagnostics(diagnostics: Option[StringBuilder]): this.type = { this.diagnostics = diagnostics; this }
    def withTypeComparerFn(tcfn: Context => TypeComparer): this.type = { this.typeComparer = tcfn(this); this }
    def withMoreProperties(moreProperties: Map[String, Any]): this.type = { this.moreProperties = moreProperties; this }

    def withProperty(prop: (String, Any)): this.type = withMoreProperties(moreProperties + prop)

    def withPhase(pid: PhaseId): this.type = withPeriod(Period(runId, pid))

    def withSetting[T](setting: Setting[T], value: T): this.type =
      withSettings(setting.updateIn(sstate, value))

    def withDebug = withSetting(base.settings.debug, true)
  }

  /** A class defining the initial context with given context base
   *  and set of possible settings.
   */
  private class InitialContext(val base: ContextBase, settings: SettingGroup) extends FreshContext {
    outer = NoContext
    period = InitialPeriod
    mode = Mode.None
    typerState = new TyperState(new ConsoleReporter()(this))
    printerFn = new RefinedPrinter(_)
    owner = NoSymbol
    sstate = settings.defaultState
    tree = untpd.EmptyTree
    runInfo = new RunInfo
    diagnostics = None
    moreProperties = Map.empty
    typeComparer = new TypeComparer(this)
  }

  object NoContext extends Context {
    lazy val base = unsupported("base")
    override def implicits: ContextualImplicits = new ContextualImplicits(Set(), this)(this)
  }

  /** A context base defines state and associated methods that exist once per
   *  compiler run.
   */
  class ContextBase extends ContextState
                       with Transformers.TransformerBase
                       with Denotations.DenotationsBase
                       with Phases.PhasesBase {

    /** The applicable settings */
    val settings = new ScalaSettings

    /** The initial context */
    val initialCtx: Context = new InitialContext(this, settings)

    /** The symbol loaders */
    val loaders = new SymbolLoaders

    /** The platform */
    val platform: Platform = new JavaPlatform

    /** The standard fresh name creator */
    val freshNames = new FreshNameCreator.Default

    def freshName(prefix: String = ""): String = freshNames.newName(prefix)
    def freshName(prefix: Name): String = freshName(prefix.toString)

    /** The loader that loads the members of _root_ */
    def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = platform.rootLoader(root)

    // Set up some phases to get started */
    usePhases(SomePhase :: Nil)

    /** The standard definitions */
    val definitions = new Definitions()(initialCtx)
  }

  /** The essential mutable state of a context base, collected into a common class */
  class ContextState {

    // Symbols state

    /** A counter for unique ids */
    private[core] var _nextId = 0

    def nextId = { _nextId += 1; _nextId }

    /** A map from a superclass id to the typeref of the class that has it */
    private[core] var classOfId = new Array[TypeRef](InitialSuperIdsSize)

    /** A map from a the typeref of a class to its superclass id */
    private[core] val superIdOfClass = new mutable.HashMap[TypeRef, Int]

    /** The last allocated superclass id */
    private[core] var lastSuperId = -1

    /** Allocate and return next free superclass id */
    private[core] def nextSuperId: Int = {
      lastSuperId += 1;
      if (lastSuperId >= classOfId.length) {
        val tmp = new Array[TypeRef](classOfId.length * 2)
        classOfId.copyToArray(tmp)
        classOfId = tmp
      }
      lastSuperId
    }

    // SymDenotations state
    /** A table where unique superclass bits are kept.
     *  These are bitsets that contain the superclass ids of all base classes of a class.
     *  Used to speed up isSubClass tests.
     */
    private[core] val uniqueBits = new util.HashSet[BitSet]("superbits", 1024)

    // Types state
    /** A table for hash consing unique types */
    private[core] val uniques = new util.HashSet[Type]("uniques", initialUniquesCapacity) {
      override def hash(x: Type): Int = x.hash
    }

    /** The number of recursive invocation of underlying on a NamedType
     *  during a controlled operation.
     */
    private[core] var underlyingRecursions: Int = 0

    /** The set of named types on which a currently active invocation
     *  of underlying during a controlled operation exists. */
    private[core] val pendingUnderlying = new mutable.HashSet[Type]

    // Phases state
    /** Phases by id */
    private[core] var phases: Array[Phase] = _

    // Printers state
    /** Number of recursive invocations of a show method on cuyrrent stack */
    private[dotc] var toTextRecursions = 0

    // Reporters state
    private[dotc] var indent = 0

    protected[dotc] val indentTab = "  "

    /** Should warnings and errors containing non-sensical strings be suppressed? */
    private[dotc] var suppressNonSensicalErrors = true
  }

  object Context {

    /** Implicit conversion that injects all printer operations into a context */
    implicit def toPrinter(ctx: Context) = ctx.printer

    /** implicit conversion that injects all ContextBase members into a context */
    implicit def toBase(ctx: Context): ContextBase = ctx.base

    val theBase = new ContextBase // !!! DEBUG, so that we can use a minimal context for reporting even in code that normallly cannot access a context
  }

  /** Info that changes on each compiler run */
  class RunInfo(implicit val ctx: Context) extends ImplicitRunInfo

  /** Initial size of superId table */
  private final val InitialSuperIdsSize = 4096

  /** Initial capacity of uniques HashMap */
  private[core] final val initialUniquesCapacity = 50000

  /** How many recursive calls to NamedType#underlying are performed before
   *  logging starts.
   */
  private[core] final val LogPendingUnderlyingThreshold = 50

  /** How many recursive calls to isSubType are performed before
   *  logging starts.
   */
  private[core] final val LogPendingSubTypesThreshold = 50
}