Skip to content

Commit f66f335

Browse files
committed
Refactor check and implement promotion
- Make summary a proper class - Rename isInternal to hasSource - Enable hijacking check - Refactor: remove useless parameter - Check promotion of potentials - Cache safely promoted potential - Fix test case tests/init/neg/hybrid2.scala - Split effect check into multiple methods - Refactor ignored methods handling Ignore constructor call on Any, Object and AnyVal - Add promotion tests - Fix missing return - Member promotion result override outer promotion result
1 parent be0898f commit f66f335

24 files changed

+561
-377
lines changed

compiler/src/dotty/tools/dotc/transform/init/Checker.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class Checker extends MiniPhase {
4747
// A concrete class may not be instantiated if the self type is not satisfied
4848
if (instantiable) {
4949
implicit val state: Checking.State = Checking.State(
50-
visited = Set.empty,
50+
checked = Set.empty,
5151
path = Vector.empty,
5252
thisClass = cls,
5353
fieldsInited = mutable.Set.empty,

compiler/src/dotty/tools/dotc/transform/init/Checking.scala

Lines changed: 269 additions & 160 deletions
Large diffs are not rendered by default.

compiler/src/dotty/tools/dotc/transform/init/Env.scala

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,20 @@ import scala.collection.mutable
1818

1919
import Effects._, Potentials._, Summary._
2020

21-
implicit def theCtx(implicit env: Env): Context = env.ctx
21+
given theCtx(using Env): Context = summon[Env].ctx
2222

2323
case class Env(ctx: Context) {
2424
private implicit def self: Env = this
2525

26-
// Methods that should be ignored in the checking
27-
lazy val ignoredMethods: Set[Symbol] = Set(
28-
defn.Any_getClass,
29-
defn.Any_isInstanceOf,
30-
defn.Object_eq,
31-
defn.Object_ne,
32-
defn.Object_synchronized
33-
)
26+
/** Can the method call be ignored? */
27+
def canIgnoreMethod(symbol: Symbol): Boolean =
28+
!symbol.exists || // possible with outer selection, tests/init/crash/i1990b.scala
29+
canIgnoreClass(symbol.owner)
30+
31+
def canIgnoreClass(cls: Symbol): Boolean =
32+
cls == defn.AnyClass ||
33+
cls == defn.AnyValClass ||
34+
cls == defn.ObjectClass
3435

3536
def withCtx(newCtx: Context): Env = this.copy(ctx = newCtx)
3637

compiler/src/dotty/tools/dotc/transform/init/Errors.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import Types._, Symbols._, Contexts._
1212
import Effects._, Potentials._
1313

1414
object Errors {
15-
type Errors = Set[Error]
16-
val empty: Errors = Set.empty
15+
type Errors = List[Error]
16+
val empty: Errors = Nil
1717

1818
def show(errs: Errors)(using Context): String =
1919
errs.map(_.show).mkString(", ")
@@ -26,7 +26,7 @@ object Errors {
2626
def issue(using Context): Unit =
2727
report.warning(show + stacktrace, source.srcPos)
2828

29-
def toErrors: Errors = Set(this)
29+
def toErrors: Errors = this :: Nil
3030

3131
def stacktrace(using Context): String = if (trace.isEmpty) "" else " Calling trace:\n" + {
3232
var indentCount = 0
@@ -55,15 +55,15 @@ object Errors {
5555
*/
5656
def flatten: Errors = this match {
5757
case unsafe: UnsafePromotion => unsafe.errors.flatMap(_.flatten)
58-
case _ => Set(this)
58+
case _ => this :: Nil
5959
}
6060
}
6161

6262
/** Access non-initialized field */
6363
case class AccessNonInit(field: Symbol, trace: Vector[Tree]) extends Error {
6464
def source: Tree = trace.last
6565
def show(using Context): String =
66-
"Access non-initialized field " + field.name.show + "."
66+
"Access non-initialized " + field.show + "."
6767

6868
override def issue(using Context): Unit =
6969
report.warning(show + stacktrace, field.srcPos)

compiler/src/dotty/tools/dotc/transform/init/Potentials.scala

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,37 @@ object Potentials {
3232
def source: Tree
3333
}
3434

35-
/** The object pointed by `this` */
36-
case class ThisRef()(val source: Tree) extends Potential {
37-
def show(using Context): String = "this"
38-
35+
sealed trait Refinable extends Potential {
3936
/** Effects of a method call or a lazy val access
37+
*
38+
* The method performs prefix substitution
4039
*/
4140
def effectsOf(sym: Symbol)(implicit env: Env): Effects = trace("effects of " + sym.show, init, r => Effects.show(r.asInstanceOf)) {
4241
val cls = sym.owner.asClass
43-
env.summaryOf(cls).effectsOf(sym)
42+
val effs = env.summaryOf(cls).effectsOf(sym)
43+
this match
44+
case _: ThisRef => effs
45+
case _ => Effects.asSeenFrom(effs, this)
4446
}
4547

4648
/** Potentials of a field, a method call or a lazy val access
49+
*
50+
* The method performs prefix substitution
4751
*/
4852
def potentialsOf(sym: Symbol)(implicit env: Env): Potentials = trace("potentials of " + sym.show, init, r => Potentials.show(r.asInstanceOf)) {
4953
val cls = sym.owner.asClass
50-
env.summaryOf(cls).potentialsOf(sym)
54+
val pots = env.summaryOf(cls).potentialsOf(sym)
55+
this match
56+
case _: ThisRef => pots
57+
case _ => Potentials.asSeenFrom(pots, this)
5158
}
5259
}
5360

61+
/** The object pointed by `this` */
62+
case class ThisRef()(val source: Tree) extends Refinable {
63+
def show(using Context): String = "this"
64+
}
65+
5466
/** The object pointed by `C.super.this`, mainly used for override resolution */
5567
case class SuperRef(pot: Potential, supercls: ClassSymbol)(val source: Tree) extends Potential {
5668
override def size: Int = pot.size
@@ -64,30 +76,10 @@ object Potentials {
6476
* @param classSymbol The concrete class of the object
6577
* @param outer The potential for `this` of the enclosing class
6678
*/
67-
case class Warm(classSymbol: ClassSymbol, outer: Potential)(val source: Tree) extends Potential {
79+
case class Warm(classSymbol: ClassSymbol, outer: Potential)(val source: Tree) extends Refinable {
6880
override def level: Int = 1 + outer.level
6981
def show(using Context): String = "Warm[" + classSymbol.show + ", outer = " + outer.show + "]"
7082

71-
/** Effects of a method call or a lazy val access
72-
*
73-
* The method performs prefix substitution
74-
*/
75-
def effectsOf(sym: Symbol)(implicit env: Env): Effects = trace("effects of " + sym.show, init, r => Effects.show(r.asInstanceOf)) {
76-
val cls = sym.owner.asClass
77-
val effs = env.summaryOf(cls).effectsOf(sym)
78-
Effects.asSeenFrom(effs, this)
79-
}
80-
81-
/** Potentials of a field, a method call or a lazy val access
82-
*
83-
* The method performs prefix substitution
84-
*/
85-
def potentialsOf(sym: Symbol)(implicit env: Env): Potentials = trace("potentials of " + sym.show, init, r => Potentials.show(r.asInstanceOf)) {
86-
val cls = sym.owner.asClass
87-
val pots = env.summaryOf(cls).potentialsOf(sym)
88-
Potentials.asSeenFrom(pots, this)
89-
}
90-
9183
def resolveOuter(cls: ClassSymbol)(implicit env: Env): Potentials =
9284
env.resolveOuter(this, cls)
9385
}
@@ -117,7 +109,7 @@ object Potentials {
117109
case class Outer(pot: Potential, classSymbol: ClassSymbol)(val source: Tree) extends Potential {
118110
// be lenient with size of outer selection, no worry for non-termination
119111
override def size: Int = pot.size
120-
override def level: Int = pot.size
112+
override def level: Int = pot.level
121113
def show(using Context): String = pot.show + ".outer[" + classSymbol.show + "]"
122114
}
123115

@@ -126,7 +118,7 @@ object Potentials {
126118
assert(field != NoSymbol)
127119

128120
override def size: Int = potential.size + 1
129-
override def level: Int = potential.size
121+
override def level: Int = potential.level
130122
def show(using Context): String = potential.show + "." + field.name.show
131123
}
132124

@@ -135,7 +127,7 @@ object Potentials {
135127
assert(method != NoSymbol)
136128

137129
override def size: Int = potential.size + 1
138-
override def level: Int = potential.size
130+
override def level: Int = potential.level
139131
def show(using Context): String = potential.show + "." + method.name.show
140132
}
141133

@@ -163,20 +155,20 @@ object Potentials {
163155
extension (pot: Potential) def toPots: Potentials = Potentials.empty + pot
164156

165157
extension (ps: Potentials) def select (symbol: Symbol, source: Tree)(using Context): Summary =
166-
ps.foldLeft(Summary.empty) { case ((pots, effs), pot) =>
158+
ps.foldLeft(Summary.empty) { case (Summary(pots, effs), pot) =>
167159
// max potential length
168160
// TODO: it can be specified on a project basis via compiler options
169161
if (pot.size > 2)
170-
(pots, effs + Promote(pot)(source))
162+
summary + Promote(pot)(pot.source)
171163
else if (symbol.isConstructor)
172-
(pots + pot, effs + MethodCall(pot, symbol)(source))
164+
Summary(pots + pot, effs + MethodCall(pot, symbol)(source))
173165
else if (symbol.isOneOf(Flags.Method | Flags.Lazy))
174-
(
166+
Summary(
175167
pots + MethodReturn(pot, symbol)(source),
176168
effs + MethodCall(pot, symbol)(source)
177169
)
178170
else
179-
(pots + FieldReturn(pot, symbol)(source), effs + FieldAccess(pot, symbol)(source))
171+
Summary(pots + FieldReturn(pot, symbol)(source), effs + FieldAccess(pot, symbol)(source))
180172
}
181173

182174
extension (ps: Potentials) def promote(source: Tree): Effects = ps.map(Promote(_)(source))
@@ -205,13 +197,11 @@ object Potentials {
205197

206198
case Warm(cls, outer2) =>
207199
// widening to terminate
208-
val thisValue2 = thisValue match {
209-
case Warm(cls, outer) if outer.level > 2 =>
210-
Warm(cls, Cold()(outer2.source))(thisValue.source)
211-
212-
case _ =>
200+
val thisValue2 =
201+
if thisValue.level + outer2.level > 4 then
202+
Cold()(outer2.source)
203+
else
213204
thisValue
214-
}
215205

216206
val outer3 = asSeenFrom(outer2, thisValue2)
217207
Warm(cls, outer3)(pot.source)

0 commit comments

Comments
 (0)