Skip to content

Commit 08ad92e

Browse files
authored
Merge pull request #15722 from dotty-staging/init-bootstrap
Bootstrap compiler with -Ysafe-init
2 parents 82e6911 + 1997628 commit 08ad92e

File tree

8 files changed

+133
-24
lines changed

8 files changed

+133
-24
lines changed

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,11 @@ class Checker extends Phase {
3131

3232
override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
3333
val checkCtx = ctx.fresh.setPhase(this.start)
34-
Semantic.withInitialState {
34+
Semantic.checkTasks(using checkCtx) {
3535
val traverser = new InitTreeTraverser()
3636
units.foreach { unit => traverser.traverse(unit.tpdTree) }
37-
given Context = checkCtx
38-
Semantic.check()
39-
super.runOn(units)
4037
}
38+
units
4139

4240
def run(using Context): Unit = {
4341
// ignore, we already called `Semantic.check()` in `runOn`

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

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,11 +1131,6 @@ object Semantic:
11311131
*
11321132
*/
11331133
def tryPromote(msg: String): Contextual[List[Error]] = log("promote " + warm.show + ", promoted = " + promoted, printer) {
1134-
val classRef = warm.klass.appliedRef
1135-
val hasInnerClass = classRef.memberClasses.filter(_.symbol.hasSource).nonEmpty
1136-
if hasInnerClass then
1137-
return PromoteError(msg + "Promotion cancelled as the value contains inner classes.", trace.toVector) :: Nil
1138-
11391134
val obj = warm.objekt
11401135

11411136
def doPromote(klass: ClassSymbol, subClass: ClassSymbol, subClassSegmentHot: Boolean)(using Reporter): Unit =
@@ -1148,7 +1143,9 @@ object Semantic:
11481143
params.forall(param => obj.field(param).isHot)
11491144
}
11501145

1151-
// check invariant: subClassSegmentHot => isHotSegment
1146+
// Check invariant: subClassSegmentHot ==> isHotSegment
1147+
//
1148+
// This invariant holds because of the Scala/Java/JVM restriction that we cannot use `this` in super constructor calls.
11521149
if subClassSegmentHot && !isHotSegment then
11531150
report.error("[Internal error] Expect current segment to hot in promotion, current klass = " + klass.show +
11541151
", subclass = " + subClass.show + Trace.show, Trace.position)
@@ -1158,7 +1155,10 @@ object Semantic:
11581155
// those methods are checked as part of the check for the class where they are defined.
11591156
if !isHotSegment then
11601157
for member <- klass.info.decls do
1161-
if !member.isType && !member.isConstructor && member.hasSource && !member.is(Flags.Deferred) then
1158+
if member.isClass then
1159+
val error = PromoteError("Promotion cancelled as the value contains inner " + member.show + ".", Vector.empty)
1160+
reporter.report(error)
1161+
else if !member.isType && !member.isConstructor && !member.is(Flags.Deferred) then
11621162
given Trace = Trace.empty
11631163
if member.is(Flags.Method, butNot = Flags.Accessor) then
11641164
val args = member.info.paramInfoss.flatten.map(_ => ArgInfo(Hot, Trace.empty))
@@ -1261,22 +1261,17 @@ object Semantic:
12611261
/** Add a checking task to the work list */
12621262
def addTask(thisRef: ThisRef)(using WorkList) = workList.addTask(Task(thisRef))
12631263

1264-
/** Perform check on the work list until it becomes empty
1265-
*
1266-
* Should only be called once from the checker.
1267-
*/
1268-
def check()(using Cache, WorkList, Context) = workList.work()
1269-
1270-
/** Perform actions with initial checking state.
1264+
/** Check the specified tasks
12711265
*
1272-
* Semantic.withInitialState {
1266+
* Semantic.checkTasks {
12731267
* Semantic.addTask(...)
1274-
* ...
1275-
* Semantic.check()
12761268
* }
12771269
*/
1278-
def withInitialState[T](work: (Cache, WorkList) ?=> T): T =
1279-
work(using new Cache, new WorkList)
1270+
def checkTasks(using Context)(taskBuilder: WorkList ?=> Unit): Unit =
1271+
val workList = new WorkList
1272+
val cache = new Cache
1273+
taskBuilder(using workList)
1274+
workList.work()(using cache, ctx)
12801275

12811276
// ----- Semantic definition --------------------------------
12821277

project/Build.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ object Build {
798798
},
799799

800800
// Note: bench/profiles/projects.yml should be updated accordingly.
801-
Compile / scalacOptions ++= Seq("-Yexplicit-nulls"),
801+
Compile / scalacOptions ++= Seq("-Yexplicit-nulls", "-Ysafe-init"),
802802

803803
repl := (Compile / console).value,
804804
Compile / console / scalacOptions := Nil, // reset so that we get stock REPL behaviour! E.g. avoid -unchecked being enabled
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- Error: tests/init/neg/promotion-segment3.scala:9:6 ------------------------------------------------------------------
2+
9 | bar(new B) // error
3+
| ^^^^^
4+
| Cannot prove the method argument is hot. Only hot values are safe to leak.
5+
| Found = Warm[class B] { outer = ThisRef[class A] }. Calling trace:
6+
| -> class A: [ promotion-segment3.scala:2 ]
7+
| ^
8+
| -> bar(new B) // error [ promotion-segment3.scala:9 ]
9+
| ^^^^^
10+
|
11+
| Promoting the value to hot failed due to the following problem:
12+
| Promotion cancelled as the value contains inner class C.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
class A:
3+
class B:
4+
class C:
5+
def foo(): Int = m
6+
7+
def bar(b: B) = new b.C().foo()
8+
9+
bar(new B) // error
10+
11+
val m = 10
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
-- Error: tests/init/neg/super-resolution3.scala:27:6 ------------------------------------------------------------------
2+
27 | val n = 40 // error
3+
| ^
4+
| Access non-initialized value n. Calling trace:
5+
| -> class C extends A with M with N: [ super-resolution3.scala:22 ]
6+
| ^
7+
| -> new Inner() [ super-resolution3.scala:23 ]
8+
| ^^^^^^^^^^^
9+
| -> class Inner: [ super-resolution3.scala:17 ]
10+
| ^
11+
| -> N.super[A].foo() [ super-resolution3.scala:18 ]
12+
| ^^^^^^^^^^^^^^^^
13+
| -> def foo(): Int = n [ super-resolution3.scala:3 ]
14+
| ^
15+
-- Error: tests/init/neg/super-resolution3.scala:26:6 ------------------------------------------------------------------
16+
26 | val m = 30 // error
17+
| ^
18+
| Access non-initialized value m. Calling trace:
19+
| -> class C extends A with M with N: [ super-resolution3.scala:22 ]
20+
| ^
21+
| -> new Inner() [ super-resolution3.scala:23 ]
22+
| ^^^^^^^^^^^
23+
| -> class Inner: [ super-resolution3.scala:17 ]
24+
| ^
25+
| -> N.super.foo() [ super-resolution3.scala:19 ]
26+
| ^^^^^^^^^^^^^
27+
| -> override def foo(): Int = a + super.foo() [ super-resolution3.scala:11 ]
28+
| ^^^^^^^^^^^
29+
| -> def foo(): Int = m [ super-resolution3.scala:7 ]
30+
| ^
31+
-- Error: tests/init/neg/super-resolution3.scala:24:6 ------------------------------------------------------------------
32+
24 | val a = 10 // error
33+
| ^
34+
| Access non-initialized value a. Calling trace:
35+
| -> class C extends A with M with N: [ super-resolution3.scala:22 ]
36+
| ^
37+
| -> new Inner() [ super-resolution3.scala:23 ]
38+
| ^^^^^^^^^^^
39+
| -> class Inner: [ super-resolution3.scala:17 ]
40+
| ^
41+
| -> N.super.foo() [ super-resolution3.scala:19 ]
42+
| ^^^^^^^^^^^^^
43+
| -> override def foo(): Int = a + super.foo() [ super-resolution3.scala:11 ]
44+
| ^
45+
-- Error: tests/init/neg/super-resolution3.scala:25:6 ------------------------------------------------------------------
46+
25 | val b = 20 // error
47+
| ^
48+
| Access non-initialized value b. Calling trace:
49+
| -> class C extends A with M with N: [ super-resolution3.scala:22 ]
50+
| ^
51+
| -> new Inner() [ super-resolution3.scala:23 ]
52+
| ^^^^^^^^^^^
53+
| -> class Inner: [ super-resolution3.scala:17 ]
54+
| ^
55+
| -> foo() [ super-resolution3.scala:20 ]
56+
| ^^^^^
57+
| -> override def foo(): Int = b * super.foo() [ super-resolution3.scala:15 ]
58+
| ^
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
abstract class A:
2+
val n: Int
3+
def foo(): Int = n
4+
5+
trait B:
6+
val m: Int
7+
def foo(): Int = m
8+
9+
trait M extends A with B:
10+
val a: Int
11+
override def foo(): Int = a + super.foo()
12+
13+
trait N extends A with B:
14+
val b: Int
15+
override def foo(): Int = b * super.foo()
16+
17+
class Inner:
18+
N.super[A].foo()
19+
N.super.foo()
20+
foo()
21+
22+
class C extends A with M with N:
23+
new Inner()
24+
val a = 10 // error
25+
val b = 20 // error
26+
val m = 30 // error
27+
val n = 40 // error

tests/init/pos/DottyTest.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class ContextBase:
2+
class Inner:
3+
def foo(): ContextBase = ContextBase.this
4+
5+
class Outer:
6+
val ctx = new ContextBase {}
7+
println(ctx)
8+
val n = 10

0 commit comments

Comments
 (0)