Skip to content

Commit 7f1ae28

Browse files
authored
Merge pull request #15488 from dotty-staging/fix-15459
Fix #15459: Display uninitialized fields in promotion error
2 parents 9e3953d + 227616c commit 7f1ae28

File tree

7 files changed

+59
-19
lines changed

7 files changed

+59
-19
lines changed

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,19 @@ object Semantic:
10061006
}
10071007
}
10081008

1009+
def nonInitFields(): Contextual[List[Symbol]] =
1010+
val obj = ref.objekt
1011+
ref.klass.baseClasses.flatMap { klass =>
1012+
if klass.hasSource then
1013+
klass.info.decls.filter { member =>
1014+
!member.isOneOf(Flags.Method | Flags.Lazy | Flags.Deferred)
1015+
&& !member.isType
1016+
&& !obj.hasField(member)
1017+
}
1018+
else
1019+
Nil
1020+
}
1021+
10091022
end extension
10101023

10111024
extension (thisRef: ThisRef)
@@ -1032,8 +1045,12 @@ object Semantic:
10321045
reporter.report(PromoteError(msg, trace.toVector))
10331046

10341047
case thisRef: ThisRef =>
1035-
if !thisRef.tryPromoteCurrentObject() then
1036-
reporter.report(PromoteError(msg, trace.toVector))
1048+
val emptyFields = thisRef.nonInitFields()
1049+
if emptyFields.isEmpty then
1050+
promoted.promoteCurrent(thisRef)
1051+
else
1052+
val fields = "Non initialized field(s): " + emptyFields.map(_.show).mkString(", ") + "."
1053+
reporter.report(PromoteError(msg + "\n" + fields, trace.toVector))
10371054

10381055
case warm: Warm =>
10391056
if !promoted.contains(warm) then

tests/init/neg/closureLeak.check

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
| ^^^^^^^^^^^^^^^^^
99
|
1010
| Promoting the value to fully initialized failed due to the following problem:
11-
| Cannot prove the argument is fully initialized. Only fully initialized values are safe to leak. Calling trace:
11+
| Cannot prove the argument is fully initialized. Only fully initialized values are safe to leak.
12+
| Non initialized field(s): value p. Calling trace:
1213
| -> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ]
1314
| ^^^^

tests/init/neg/default-this.check

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
-- Error: tests/init/neg/default-this.scala:9:8 ------------------------------------------------------------------------
22
9 | compare() // error
33
| ^^^^^^^
4-
| Cannot prove the argument is fully initialized. Only fully initialized values are safe to leak. Calling trace:
5-
| -> class B extends A { [ default-this.scala:6 ]
6-
| ^
7-
| -> val result = updateThenCompare(5) [ default-this.scala:11 ]
8-
| ^^^^^^^^^^^^^^^^^^^^
9-
| -> def updateThenCompare(c: Int): Boolean = { [ default-this.scala:7 ]
10-
| ^
11-
| -> compare() // error [ default-this.scala:9 ]
12-
| ^^^^^^^
4+
| Cannot prove the argument is fully initialized. Only fully initialized values are safe to leak.
5+
| Non initialized field(s): value result. Calling trace:
6+
| -> class B extends A { [ default-this.scala:6 ]
7+
| ^
8+
| -> val result = updateThenCompare(5) [ default-this.scala:11 ]
9+
| ^^^^^^^^^^^^^^^^^^^^
10+
| -> def updateThenCompare(c: Int): Boolean = { [ default-this.scala:7 ]
11+
| ^
12+
| -> compare() // error [ default-this.scala:9 ]
13+
| ^^^^^^^

tests/init/neg/i15459.check

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- Error: tests/init/neg/i15459.scala:3:10 -----------------------------------------------------------------------------
2+
3 | println(this) // error
3+
| ^^^^
4+
| Cannot prove the argument is fully initialized. Only fully initialized values are safe to leak.
5+
| Non initialized field(s): value b. Calling trace:
6+
| -> class Sub extends Sup: [ i15459.scala:5 ]
7+
| ^
8+
| -> class Sup: [ i15459.scala:1 ]
9+
| ^
10+
| -> println(this) // error [ i15459.scala:3 ]
11+
| ^^^^

tests/init/neg/i15459.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class Sup:
2+
val a = 10
3+
println(this) // error
4+
5+
class Sub extends Sup:
6+
val b = 20
7+
8+
override def toString() = "a = " + a + ", b = " + b

tests/init/neg/inlined-method.check

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
-- Error: tests/init/neg/inlined-method.scala:8:45 ---------------------------------------------------------------------
22
8 | scala.runtime.Scala3RunTime.assertFailed(message) // error
33
| ^^^^^^^
4-
| Cannot prove the argument is fully initialized. Only fully initialized values are safe to leak. Calling trace:
5-
| -> class InlineError { [ inlined-method.scala:1 ]
6-
| ^
7-
| -> Assertion.failAssert(this) [ inlined-method.scala:2 ]
8-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
9-
| -> scala.runtime.Scala3RunTime.assertFailed(message) // error [ inlined-method.scala:8 ]
10-
| ^^^^^^^
4+
| Cannot prove the argument is fully initialized. Only fully initialized values are safe to leak.
5+
| Non initialized field(s): value v. Calling trace:
6+
| -> class InlineError { [ inlined-method.scala:1 ]
7+
| ^
8+
| -> Assertion.failAssert(this) [ inlined-method.scala:2 ]
9+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
10+
| -> scala.runtime.Scala3RunTime.assertFailed(message) // error [ inlined-method.scala:8 ]
11+
| ^^^^^^^

tests/init/neg/promotion-loop.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
|
1010
| Promoting the value to fully initialized failed due to the following problem:
1111
| Cannot prove that the field val outer is fully initialized.
12+
| Non initialized field(s): value n.

0 commit comments

Comments
 (0)