dotty.tools.dotc.transform.init

Members list

Type members

Classlikes

class Cache[Config, Res]

The co-inductive cache used for analysis

The co-inductive cache used for analysis

The cache contains two maps from (Config, Tree) to Res:

  • input cache (this.last)
  • output cache (this.current)

The two caches are required because we want to make sure in a new iteration, an expression is evaluated exactly once. The monotonicity of the analysis ensures that the cache state goes up the lattice of the abstract domain, consequently the algorithm terminates.

The general skeleton for usage of the cache is as follows

def analysis(entryExp: Expr) = {
  def iterate(entryExp: Expr)(using Cache) =
     eval(entryExp, initConfig)
     if cache.hasChanged && noErrors then
       cache.last = cache.current
       cache.current = Empty
       cache.changed = false
       iterate(entryExp)
     else
       reportErrors


  def eval(expr: Expr, config: Config)(using Cache) =
    cache.cachedEval(config, expr) {
      // Actual recursive evaluation of expression.
      //
      // Only executed if the entry `(exp, config)` is not in the output cache.
    }

  iterate(entryExp)(using new Cache)
}

See the documentation for the method Cache.cachedEval for more information.

What goes to the configuration (Config) and what goes to the result (Res) need to be decided by the specific analysis and justified by reasoning about soundness.

Type parameters

Config

The analysis state that matters for evaluating an expression.

Res

The result from the evaluation the given expression.

Attributes

Companion
object
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class Data
class Data
object Cache

Attributes

Companion
class
Supertypes
class Object
trait Matchable
class Any
Self type
Cache.type
class Checker extends Phase

Attributes

Companion
object
Supertypes
class Phase
class Object
trait Matchable
class Any
object Checker

Attributes

Companion
class
Supertypes
class Object
trait Matchable
class Any
Self type
Checker.type
object Errors

Attributes

Supertypes
class Object
trait Matchable
class Any
Self type
Errors.type
object Objects

Check initialization safety of static objects

Check initialization safety of static objects

The problem is illustrated by the example below:

class Foo(val opposite: Foo)
case object A extends Foo(B)     // A -> B
case object B extends Foo(A)     // B -> A

In the code above, the initialization of object A depends on B and vice versa. There is no correct way to initialize the code above. The current checker issues a warning for the code above.

At the high-level, the analysis has the following characteristics:

  1. The check enforces the principle of "initialization-time irrelevance", which means that the time when an object is initialized should not change program semantics. For that purpose, it enforces the following rule:

    The initialization of a static object should not directly or indirectly read or write
    mutable state of another static object.
    

    This principle not only put initialization of static objects on a solid foundation, but also avoids whole-program analysis.

  2. The design is based on the concept of "cold aliasing" --- a cold alias may not be actively used during initialization, i.e., it's forbidden to call methods or access fields of a cold alias. Method arguments are cold aliases by default unless specified to be sensitive. Method parameters captured in lambdas or inner classes are always cold aliases.

  3. It is inter-procedural and flow-sensitive.

  4. It is object-sensitive by default and parameter-sensitive on-demand.

  5. The check is modular in the sense that each object is checked separately and there is no whole-program analysis. However, the check is not modular in terms of project boundaries.

Attributes

Supertypes
class Object
trait Matchable
class Any
Self type
Objects.type
object Semantic

Checks safe initialization of objects

Checks safe initialization of objects

This algorithm cannot handle safe access of global object names. That part is handled by the check in Objects (@see Objects).

Attributes

Supertypes
class Object
trait Matchable
class Any
Self type
Semantic.type
opaque object Trace

Logic related to evaluation trace for showing friendly error messages

Logic related to evaluation trace for showing friendly error messages

A trace is a sequence of program positions which tells the evaluation order that leads to an error. It is usually more informative than the stack trace by tracking the exact sub-expression in the trace instead of only methods.

Attributes

Supertypes
class Object
trait Matchable
class Any
Self type
Trace.type
opaque object Util

Attributes

Supertypes
class Object
trait Matchable
class Any
Self type
Util.type