Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2703b6b

Browse files
authoredMay 27, 2025··
Modified abstract domain in global initialization checker (#23138)
This PR modifies the abstract domain of the global object initialization checker. It closely follows the abstract definitional interpreter framework, but changes the heap to map from scopes (including abstract objects or local environments) to a pair of `valsMap` and `outersMap`, both of which over-approximates the information of scope bodies of the concrete scopes they represent. [test_scala2_library_tasty]
2 parents 515c3e1 + ff0d699 commit 2703b6b

21 files changed

+573
-532
lines changed
 

‎compiler/src/dotty/tools/dotc/transform/init/Objects.scala

Lines changed: 431 additions & 479 deletions
Large diffs are not rendered by default.

‎compiler/test/dotc/neg-init-global-scala2-library-tasty.excludelist

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ global-list.scala
1515
t5366.scala
1616
mutable-read7.scala
1717
t9115.scala
18-
Color.scala
18+
Color.scala
19+
unapplySeq-implicit-arg2.scala
20+
unapplySeq-implicit-arg3.scala
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class B {}
2+
3+
class C(c: B) extends B
4+
5+
object O:
6+
def f(param: B): Int = f(new C(param))
7+
val a = f(new B)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object O:
2+
val a = m(2)
3+
def m(f: Int) =
4+
val a = f
5+
() => a
6+
7+
object O2:
8+
val func = O.a
9+
val a = func()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class A(val a: A)
2+
object B:
3+
val a: A = loop().a
4+
def loop(): A = new A(loop())
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class Outer {
2+
val f = 5
3+
class Inner extends Outer {
4+
val g = Outer.this.f
5+
}
6+
}
7+
8+
object O {
9+
def foo(i: Outer): Unit =
10+
val i2 = new i.Inner // i2.outer should always be OfClass(Outer)
11+
foo(i2)
12+
13+
foo(new Outer)
14+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class Outer {
2+
def foo = {
3+
val y = 5
4+
class C {
5+
val x = y
6+
}
7+
class D {
8+
new C
9+
}
10+
11+
new D
12+
}
13+
14+
foo
15+
16+
val n = 10 // warn
17+
}
18+
19+
object O {
20+
val c = new Outer
21+
val d: Object = c.foo
22+
}

‎tests/init-global/warn/widen.scala renamed to ‎tests/init-global/pos/lookup-outer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ object O:
1010
def bar(t: T) = {
1111
class A {
1212
class B {
13-
t.foo() // warn
13+
t.foo()
1414
}
1515

1616
val b = new B

‎tests/init-global/warn/global-cycle6.check

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,8 @@
88
| │ ^
99
| ├── object B { [ global-cycle6.scala:8 ]
1010
| │ ^
11-
| ├── val a = new A.Inner [ global-cycle6.scala:9 ]
12-
| │ ^^^^^^^^^^^
13-
| ├── class Inner { [ global-cycle6.scala:3 ]
14-
| │ ^
15-
| └── println(n) // warn [ global-cycle6.scala:4 ]
16-
| ^
11+
| └── val a = new A.Inner [ global-cycle6.scala:9 ]
12+
| ^^^^^^^^^^^
1713
-- Warning: tests/init-global/warn/global-cycle6.scala:4:12 ------------------------------------------------------------
1814
4 | println(n) // warn
1915
| ^
@@ -26,3 +22,15 @@
2622
| │ ^
2723
| └── println(n) // warn [ global-cycle6.scala:4 ]
2824
| ^
25+
-- Warning: tests/init-global/warn/global-cycle6.scala:14:9 ------------------------------------------------------------
26+
14 | object A { // warn
27+
| ^
28+
| Cyclic initialization: object A -> object B -> object A. Calling trace:
29+
| ├── object A { // warn [ global-cycle6.scala:14 ]
30+
| │ ^
31+
| ├── val n: Int = B.m [ global-cycle6.scala:15 ]
32+
| │ ^
33+
| ├── object B { [ global-cycle6.scala:21 ]
34+
| │ ^
35+
| └── val a = new A.Inner [ global-cycle6.scala:22 ]
36+
| ^^^^^^^^^^^

‎tests/init-global/warn/global-cycle6.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ object B {
1111
}
1212

1313
object O {
14-
object A {
14+
object A { // warn
1515
val n: Int = B.m
1616
class Inner {
1717
val x: Int = 4

‎tests/init-global/warn/global-irrelevance3.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@
99
|│ ^^^^^^^
1010
|└── (() => x) // warn [ global-irrelevance3.scala:9 ]
1111
| ^
12+
|The mutable state is created through:
13+
|├── object A: [ global-irrelevance3.scala:1 ]
14+
|│ ^
15+
|└── val p: Pair = foo() [ global-irrelevance3.scala:3 ]
16+
| ^^^^^

‎tests/init-global/warn/global-irrelevance4.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@
99
| │ ^^^^^^^^^
1010
| └── (y => x = y), // warn [ global-irrelevance4.scala:8 ]
1111
| ^^^^^^^^^^
12+
| The mutable state is created through:
13+
| ├── object A: [ global-irrelevance4.scala:1 ]
14+
| │ ^
15+
| └── val p: Pair = foo() [ global-irrelevance4.scala:3 ]
16+
| ^^^^^

‎tests/init-global/warn/mutable-array.check

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,5 @@
1010
|The mutable state is created through:
1111
|├── object A: [ mutable-array.scala:1 ]
1212
|│ ^
13-
|├── val box: Box = new Box(0) [ mutable-array.scala:3 ]
14-
|│ ^^^^^^^^^^
15-
|└── class Box(var value: Int) [ mutable-array.scala:2 ]
16-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
13+
|└── val box: Box = new Box(0) [ mutable-array.scala:3 ]
14+
| ^^^^^^^^^^

‎tests/init-global/warn/mutable-read1.check

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,5 @@
1010
|The mutable state is created through:
1111
|├── object A: [ mutable-read1.scala:3 ]
1212
|│ ^
13-
|├── val box: Box = new Box(4) [ mutable-read1.scala:4 ]
14-
|│ ^^^^^^^^^^
15-
|└── class Box(var value: Int) [ mutable-read1.scala:1 ]
16-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
13+
|└── val box: Box = new Box(4) [ mutable-read1.scala:4 ]
14+
| ^^^^^^^^^^

‎tests/init-global/warn/mutable-read2.check

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,5 @@
1010
|The mutable state is created through:
1111
|├── object A: [ mutable-read2.scala:1 ]
1212
|│ ^
13-
|├── val box: Box = new Box(0) [ mutable-read2.scala:5 ]
14-
|│ ^^^^^^^^^^
15-
|└── class Box(var value: Int) { [ mutable-read2.scala:2 ]
16-
| ^
13+
|└── val box: Box = new Box(0) [ mutable-read2.scala:5 ]
14+
| ^^^^^^^^^^

‎tests/init-global/warn/mutable-read3.check

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,5 @@
1010
|The mutable state is created through:
1111
|├── object A: [ mutable-read3.scala:1 ]
1212
|│ ^
13-
|├── val box: Box = new Box(0) [ mutable-read3.scala:3 ]
14-
|│ ^^^^^^^^^^
15-
|└── class Box(var value: Int) [ mutable-read3.scala:2 ]
16-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
13+
|└── val box: Box = new Box(0) [ mutable-read3.scala:3 ]
14+
| ^^^^^^^^^^

‎tests/init-global/warn/mutable-read4.check

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,5 @@
1010
|The mutable state is created through:
1111
|├── object A: [ mutable-read4.scala:3 ]
1212
|│ ^
13-
|├── val box: Box = new Box(4) [ mutable-read4.scala:4 ]
14-
|│ ^^^^^^^^^^
15-
|└── class Box(var value: Int) [ mutable-read4.scala:1 ]
16-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
13+
|└── val box: Box = new Box(4) [ mutable-read4.scala:4 ]
14+
| ^^^^^^^^^^

‎tests/init-global/warn/mutable-read6.check

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,5 @@
1212
|The mutable state is created through:
1313
|├── object Contexts: [ mutable-read6.scala:3 ]
1414
|│ ^
15-
|├── val NoContext: Context = new Context [ mutable-read6.scala:4 ]
16-
|│ ^^^^^^^^^^^
17-
|└── class Context: [ mutable-read6.scala:5 ]
18-
| ^
15+
|└── val NoContext: Context = new Context [ mutable-read6.scala:4 ]
16+
| ^^^^^^^^^^^
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- Warning: tests/init-global/warn/unsoundness.scala:12:6 --------------------------------------------------------------
2+
12 | O.x // warn
3+
| ^^^
4+
| Access uninitialized field value x. Calling trace:
5+
| ├── object O: [ unsoundness.scala:15 ]
6+
| │ ^
7+
| ├── f(if m > 5 then Box(A(3)) else Box(B(4))) [ unsoundness.scala:17 ]
8+
| │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
9+
| ├── def f(a: Box[Base[Int]]): Unit = [ unsoundness.scala:21 ]
10+
| │ ^
11+
| ├── h(a.value) [ unsoundness.scala:22 ]
12+
| │ ^^^^^^^^^^
13+
| ├── def h(a: Base[Int]): Unit = [ unsoundness.scala:24 ]
14+
| │ ^
15+
| ├── a.update(10) [ unsoundness.scala:25 ]
16+
| │ ^^^^^^^^^^^^
17+
| ├── def update(n: T) = [ unsoundness.scala:11 ]
18+
| │ ^
19+
| └── O.x // warn [ unsoundness.scala:12 ]
20+
| ^^^
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
class Box[T](val value: T)
2+
3+
abstract class Base[T]:
4+
def update(n: T): Unit
5+
6+
class A[T](var a: T) extends Base[T]:
7+
def update(n: T) =
8+
a = n
9+
10+
class B[T](var b: T) extends Base[T]:
11+
def update(n: T) =
12+
O.x // warn
13+
b = n
14+
15+
object O:
16+
val m: Int = 3
17+
f(if m > 5 then Box(A(3)) else Box(B(4)))
18+
19+
val x: Int = 10
20+
21+
def f(a: Box[Base[Int]]): Unit =
22+
h(a.value)
23+
24+
def h(a: Base[Int]): Unit =
25+
a.update(10)

‎tests/init-global/warn/widen.check

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.