Skip to content

Commit a5bb6be

Browse files
authored
Added a second trace for global init checker showing creation of mutable fields (#19996)
2 parents 18b146b + fd6aaf7 commit a5bb6be

15 files changed

+126
-16
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ class Compiler {
167167
val rctx =
168168
if ctx.settings.Xsemanticdb.value then
169169
ctx.addMode(Mode.ReadPositions)
170+
else if ctx.settings.YcheckInitGlobal.value then
171+
ctx.addMode(Mode.ReadPositions)
170172
else
171173
ctx
172174
new Run(this, rctx)

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

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ class Objects:
197197
*
198198
* @param owner The static object whose initialization creates the array.
199199
*/
200-
case class OfArray(owner: ClassSymbol, regions: Regions.Data)(using @constructorOnly ctx: Context) extends ValueElement:
200+
case class OfArray(owner: ClassSymbol, regions: Regions.Data)(using @constructorOnly ctx: Context, @constructorOnly trace: Trace) extends ValueElement:
201201
val klass: ClassSymbol = defn.ArrayClass
202202
val addr: Heap.Addr = Heap.arrayAddr(regions, owner)
203203
def show(using Context) = "OfArray(owner = " + owner.show + ")"
@@ -455,9 +455,11 @@ class Objects:
455455
abstract class Addr:
456456
/** The static object which owns the mutable slot */
457457
def owner: ClassSymbol
458+
def getTrace: Trace = Trace.empty
458459

459460
/** The address for mutable fields of objects. */
460-
private case class FieldAddr(regions: Regions.Data, field: Symbol, owner: ClassSymbol) extends Addr
461+
private case class FieldAddr(regions: Regions.Data, field: Symbol, owner: ClassSymbol)(trace: Trace) extends Addr:
462+
override def getTrace: Trace = trace
461463

462464
/** The address for mutable local variables . */
463465
private case class LocalVarAddr(regions: Regions.Data, sym: Symbol, owner: ClassSymbol) extends Addr
@@ -497,11 +499,11 @@ class Objects:
497499
def localVarAddr(regions: Regions.Data, sym: Symbol, owner: ClassSymbol): Addr =
498500
LocalVarAddr(regions, sym, owner)
499501

500-
def fieldVarAddr(regions: Regions.Data, sym: Symbol, owner: ClassSymbol): Addr =
501-
FieldAddr(regions, sym, owner)
502+
def fieldVarAddr(regions: Regions.Data, sym: Symbol, owner: ClassSymbol)(using Trace): Addr =
503+
FieldAddr(regions, sym, owner)(summon[Trace])
502504

503-
def arrayAddr(regions: Regions.Data, owner: ClassSymbol)(using Context): Addr =
504-
FieldAddr(regions, defn.ArrayClass, owner)
505+
def arrayAddr(regions: Regions.Data, owner: ClassSymbol)(using Trace, Context): Addr =
506+
FieldAddr(regions, defn.ArrayClass, owner)(summon[Trace])
505507

506508
def getHeapData()(using mutable: MutableData): Data = mutable.heap
507509

@@ -654,12 +656,12 @@ class Objects:
654656
if arr.addr.owner == State.currentObject then
655657
Heap.read(arr.addr)
656658
else
657-
errorReadOtherStaticObject(State.currentObject, arr.addr.owner)
659+
errorReadOtherStaticObject(State.currentObject, arr.addr)
658660
Bottom
659661
else if target == defn.Array_update then
660662
assert(args.size == 2, "Incorrect number of arguments for Array update, found = " + args.size)
661663
if arr.addr.owner != State.currentObject then
662-
errorMutateOtherStaticObject(State.currentObject, arr.addr.owner)
664+
errorMutateOtherStaticObject(State.currentObject, arr.addr)
663665
else
664666
Heap.writeJoin(arr.addr, args.tail.head.value)
665667
Bottom
@@ -810,7 +812,7 @@ class Objects:
810812
if addr.owner == State.currentObject then
811813
Heap.read(addr)
812814
else
813-
errorReadOtherStaticObject(State.currentObject, addr.owner)
815+
errorReadOtherStaticObject(State.currentObject, addr)
814816
Bottom
815817
else if ref.isObjectRef && ref.klass.hasSource then
816818
report.warning("Access uninitialized field " + field.show + ". " + Trace.show, Trace.position)
@@ -879,7 +881,7 @@ class Objects:
879881
if ref.hasVar(field) then
880882
val addr = ref.varAddr(field)
881883
if addr.owner != State.currentObject then
882-
errorMutateOtherStaticObject(State.currentObject, addr.owner)
884+
errorMutateOtherStaticObject(State.currentObject, addr)
883885
else
884886
Heap.writeJoin(addr, rhs)
885887
else
@@ -968,7 +970,7 @@ class Objects:
968970
if addr.owner == State.currentObject then
969971
Heap.read(addr)
970972
else
971-
errorReadOtherStaticObject(State.currentObject, addr.owner)
973+
errorReadOtherStaticObject(State.currentObject, addr)
972974
Bottom
973975
end if
974976
case _ =>
@@ -1020,7 +1022,7 @@ class Objects:
10201022
Env.getVar(sym) match
10211023
case Some(addr) =>
10221024
if addr.owner != State.currentObject then
1023-
errorMutateOtherStaticObject(State.currentObject, addr.owner)
1025+
errorMutateOtherStaticObject(State.currentObject, addr)
10241026
else
10251027
Heap.writeJoin(addr, value)
10261028
case _ =>
@@ -1757,20 +1759,31 @@ class Objects:
17571759
if cls.isAllOf(Flags.JavaInterface) then Bottom
17581760
else evalType(tref.prefix, thisV, klass, elideObjectAccess = cls.isStatic)
17591761

1762+
def printTraceWhenMultiple(trace: Trace)(using Context): String =
1763+
if trace.toVector.size > 1 then
1764+
Trace.buildStacktrace(trace, "The mutable state is created through: " + System.lineSeparator())
1765+
else ""
1766+
17601767
val mutateErrorSet: mutable.Set[(ClassSymbol, ClassSymbol)] = mutable.Set.empty
1761-
def errorMutateOtherStaticObject(currentObj: ClassSymbol, otherObj: ClassSymbol)(using Trace, Context) =
1768+
def errorMutateOtherStaticObject(currentObj: ClassSymbol, addr: Heap.Addr)(using Trace, Context) =
1769+
val otherObj = addr.owner
1770+
val addr_trace = addr.getTrace
17621771
if mutateErrorSet.add((currentObj, otherObj)) then
17631772
val msg =
17641773
s"Mutating ${otherObj.show} during initialization of ${currentObj.show}.\n" +
1765-
"Mutating other static objects during the initialization of one static object is forbidden. " + Trace.show
1774+
"Mutating other static objects during the initialization of one static object is forbidden. " + Trace.show +
1775+
printTraceWhenMultiple(addr_trace)
17661776

17671777
report.warning(msg, Trace.position)
17681778

17691779
val readErrorSet: mutable.Set[(ClassSymbol, ClassSymbol)] = mutable.Set.empty
1770-
def errorReadOtherStaticObject(currentObj: ClassSymbol, otherObj: ClassSymbol)(using Trace, Context) =
1780+
def errorReadOtherStaticObject(currentObj: ClassSymbol, addr: Heap.Addr)(using Trace, Context) =
1781+
val otherObj = addr.owner
1782+
val addr_trace = addr.getTrace
17711783
if readErrorSet.add((currentObj, otherObj)) then
17721784
val msg =
17731785
"Reading mutable state of " + otherObj.show + " during initialization of " + currentObj.show + ".\n" +
1774-
"Reading mutable state of other static objects is forbidden as it breaks initialization-time irrelevance. " + Trace.show
1786+
"Reading mutable state of other static objects is forbidden as it breaks initialization-time irrelevance. " + Trace.show +
1787+
printTraceWhenMultiple(addr_trace)
17751788

17761789
report.warning(msg, Trace.position)

tests/init-global/warn/TypeCast.check

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Warning: tests/init-global/warn/TypeCast.scala:7:17 -----------------------------------------------------------------
2+
7 | def g(): Int = f // warn
3+
| ^
4+
| Access uninitialized field value f. Calling trace:
5+
| ├── object B { [ TypeCast.scala:5 ]
6+
| │ ^
7+
| ├── val f: Int = g() [ TypeCast.scala:6 ]
8+
| │ ^^^
9+
| └── def g(): Int = f // warn [ TypeCast.scala:7 ]
10+
| ^

tests/init-global/warn/TypeCast.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
object A {
2+
val f: Int = 10
3+
def m() = f
4+
}
5+
object B {
6+
val f: Int = g()
7+
def g(): Int = f // warn
8+
}
9+
object C {
10+
val a: A.type | B.type = if ??? then A else B
11+
def cast[T](a: Any): T = a.asInstanceOf[T]
12+
val c: A.type = cast[A.type](a) // abstraction for c is {A, B}
13+
val d = c.f // treat as c.asInstanceOf[owner of f].f
14+
val e = c.m() // treat as c.asInstanceOf[owner of f].m()
15+
val c2: B.type = cast[B.type](a)
16+
val g = c2.f // no error here
17+
}
18+

tests/init-global/warn/global-irrelevance5.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@
77
|│ ^
88
|└── var y = A.array(0) * 2 // warn [ global-irrelevance5.scala:6 ]
99
| ^^^^^^^^^^
10+
|The mutable state is created through:
11+
|├── object A: [ global-irrelevance5.scala:1 ]
12+
|│ ^
13+
|└── val array: Array[Int] = new Array(1) [ global-irrelevance5.scala:2 ]
14+
| ^^^^^^^^^^^^

tests/init-global/warn/global-irrelevance6.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@
77
|│ ^
88
|└── var y = A.array(0).foo() * 2 // warn [ global-irrelevance6.scala:9 ]
99
| ^^^^^^^^^^
10+
|The mutable state is created through:
11+
|├── object A: [ global-irrelevance6.scala:4 ]
12+
|│ ^
13+
|└── val array: Array[Box] = new Array(1) [ global-irrelevance6.scala:5 ]
14+
| ^^^^^^^^^^^^

tests/init-global/warn/global-irrelevance7.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@
77
|│ ^
88
|└── var y = A.array(0).foo() * 2 // warn [ global-irrelevance7.scala:10 ]
99
| ^^^^^^^^^^
10+
|The mutable state is created through:
11+
|├── object A: [ global-irrelevance7.scala:4 ]
12+
|│ ^
13+
|└── val array: Array[Box] = new Array(1) [ global-irrelevance7.scala:5 ]
14+
| ^^^^^^^^^^^^

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@
77
|│ ^
88
|└── val x: Int = box.value // warn [ mutable-array.scala:8 ]
99
| ^^^^^^^^^
10+
|The mutable state is created through:
11+
|├── object A: [ mutable-array.scala:1 ]
12+
|│ ^
13+
|├── val box: Box = new Box(0) [ mutable-array.scala:3 ]
14+
|│ ^^^^^^^^^^
15+
|└── class Box(var value: Int) [ mutable-array.scala:2 ]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@
77
|│ ^
88
|└── val n: Int = boxA.value // warn [ mutable-read1.scala:10 ]
99
| ^^^^^^^^^^
10+
|The mutable state is created through:
11+
|├── object A: [ mutable-read1.scala:3 ]
12+
|│ ^
13+
|├── val box: Box = new Box(4) [ mutable-read1.scala:4 ]
14+
|│ ^^^^^^^^^^
15+
|└── class Box(var value: Int) [ mutable-read1.scala:1 ]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^

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

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

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@
77
|│ ^
88
|└── val x: Int = box.value // warn [ mutable-read3.scala:9 ]
99
| ^^^^^^^^^
10+
|The mutable state is created through:
11+
|├── object A: [ mutable-read3.scala:1 ]
12+
|│ ^
13+
|├── val box: Box = new Box(0) [ mutable-read3.scala:3 ]
14+
|│ ^^^^^^^^^^
15+
|└── class Box(var value: Int) [ mutable-read3.scala:2 ]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@
77
|│ ^
88
|└── val n: Int = boxA.value // warn [ mutable-read4.scala:10 ]
99
| ^^^^^^^^^^
10+
|The mutable state is created through:
11+
|├── object A: [ mutable-read4.scala:3 ]
12+
|│ ^
13+
|├── val box: Box = new Box(4) [ mutable-read4.scala:4 ]
14+
|│ ^^^^^^^^^^
15+
|└── class Box(var value: Int) [ mutable-read4.scala:1 ]
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,10 @@
99
|│ ^^^^^^^^^^^^^^^^
1010
|└── final def source: SourceFile = _source // warn [ mutable-read6.scala:7 ]
1111
| ^^^^^^^
12+
|The mutable state is created through:
13+
|├── object Contexts: [ mutable-read6.scala:3 ]
14+
|│ ^
15+
|├── val NoContext: Context = new Context [ mutable-read6.scala:4 ]
16+
|│ ^^^^^^^^^^^
17+
|└── class Context: [ mutable-read6.scala:5 ]
18+
| ^

tests/init-global/warn/patmat-unapplySeq.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@
99
|│ ^^^^
1010
|└── def apply(i: Int): Box = array(i) // warn [ patmat-unapplySeq.scala:8 ]
1111
| ^^^^^^^^
12+
|The mutable state is created through:
13+
|├── object A: [ patmat-unapplySeq.scala:1 ]
14+
|│ ^
15+
|└── val array: Array[Box] = new Array(1) [ patmat-unapplySeq.scala:4 ]
16+
| ^^^^^^^^^^^^

tests/init-global/warn/patmat-unapplySeq2.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@
99
|│ ^^^^^
1010
|└── def apply(i: Int): Box = array(i) // warn [ patmat-unapplySeq2.scala:8 ]
1111
| ^^^^^^^^
12+
|The mutable state is created through:
13+
|├── object A: [ patmat-unapplySeq2.scala:1 ]
14+
|│ ^
15+
|└── val array: Array[Box] = new Array(1) [ patmat-unapplySeq2.scala:4 ]
16+
| ^^^^^^^^^^^^

0 commit comments

Comments
 (0)