Skip to content

Commit 9a12206

Browse files
committed
Be more specific about the non-hot outer in error messages
1 parent f7e8fc0 commit 9a12206

File tree

3 files changed

+54
-44
lines changed

3 files changed

+54
-44
lines changed

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

+24-13
Original file line numberDiff line numberDiff line change
@@ -100,23 +100,34 @@ object Errors:
100100
"Promoting the value to fully initialized failed due to the following problem:\n" +
101101
error.show
102102

103-
/** Unsafe leaking a non-hot value as constructor arguments */
104-
case class UnsafeLeaking(trace: Seq[Tree], error: Error, argsIndices: List[Int]) extends Error:
103+
/** Unsafe leaking a non-hot value as constructor arguments
104+
*
105+
* Invariant: argsIndices.nonEmpty
106+
*/
107+
case class UnsafeLeaking(trace: Seq[Tree], error: Error, nonHotOuterClass: Symbol, argsIndices: List[Int]) extends Error:
105108
def show(using Context): String =
106109
"Problematic object instantiation: " + argumentInfo() + stacktrace() + "\n" +
107110
"It leads to the following error during object initialization:\n" +
108111
error.show
109112

110-
private def argumentInfo(): String =
111-
val multiple = argsIndices.size > 1
112-
val part1 =
113-
argsIndices.zipWithIndex.foldLeft("") { case (acc, (pos, i)) =>
114-
val text1 = if pos == 0 then "the outer" else "arg " + pos.toString
115-
val text2 =
116-
if i == argsIndices.size - 2 then text1 + " and "
117-
else if i == argsIndices.size - 1 then text1
118-
else text1 + ", "
113+
private def punctuation(i: Int): String =
114+
if i == argsIndices.size - 2 then " and "
115+
else if i == argsIndices.size - 1 then ""
116+
else ", "
117+
118+
private def argumentInfo()(using Context): String =
119+
val multiple = argsIndices.size > 1 || nonHotOuterClass.exists
120+
val init =
121+
if nonHotOuterClass.exists
122+
then "the outer " + nonHotOuterClass.name.show + ".this" + punctuation(-1)
123+
else ""
124+
125+
val subject =
126+
argsIndices.zipWithIndex.foldLeft(init) { case (acc, (pos, i)) =>
127+
val text1 = "arg " + pos.toString
128+
val text2 = text1 + punctuation(i)
119129
acc + text2
120130
}
121-
val part2 = if multiple then " are not fully initialized." else " is not fully initialized."
122-
part1 + part2
131+
val verb = if multiple then " are " else " is "
132+
val adjective = "not fully initialized."
133+
subject + verb + adjective

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

+4-5
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ object Semantic:
777777

778778
/** Handle a new expression `new p.C` where `p` is abstracted by `value` */
779779
def instantiate(klass: ClassSymbol, ctor: Symbol, args: List[ArgInfo]): Contextual[Value] = log("instantiating " + klass.show + ", value = " + value + ", args = " + args.map(_.value.show), printer, (_: Value).show) {
780-
def tryLeak(warm: Warm, argValues: List[Value]): Contextual[Value] =
780+
def tryLeak(warm: Warm, nonHotOuterClass: Symbol, argValues: List[Value]): Contextual[Value] =
781781
val argInfos2 = args.zip(argValues).map { (argInfo, v) => argInfo.copy(value = v) }
782782
val errors = Reporter.stopEarly {
783783
given Trace = Trace.empty
@@ -791,8 +791,7 @@ object Semantic:
791791
yield
792792
i + 1
793793

794-
val indices2 = if warm.outer.isHot then indices else 0 :: indices
795-
val error = UnsafeLeaking(trace.toVector, errors.head, indices2)
794+
val error = UnsafeLeaking(trace.toVector, errors.head, nonHotOuterClass, indices)
796795
reporter.report(error)
797796
Hot
798797
else
@@ -814,7 +813,7 @@ object Semantic:
814813
else
815814
val outer = Hot
816815
val warm = Warm(klass, outer, ctor, args2).ensureObjectExists()
817-
tryLeak(warm, args2)
816+
tryLeak(warm, NoSymbol, args2)
818817

819818
case Cold =>
820819
val error = CallCold(ctor, trace.toVector)
@@ -832,7 +831,7 @@ object Semantic:
832831
val argsWidened = args.map(_.value).widenArgs
833832
val warm = Warm(klass, outer, ctor, argsWidened).ensureObjectExists()
834833
if argsWidened.exists(_.isCold) then
835-
tryLeak(warm, argsWidened)
834+
tryLeak(warm, klass.owner.lexicallyEnclosingClass, argsWidened)
836835
else
837836
val argInfos2 = args.zip(argsWidened).map { (argInfo, v) => argInfo.copy(value = v) }
838837
warm.callConstructor(ctor, argInfos2)

tests/init/neg/secondary-ctor4.check

+26-26
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
11
-- Error: tests/init/neg/secondary-ctor4.scala:54:14 -------------------------------------------------------------------
22
54 | val c = new C(b, 5) // error
33
| ^^^^^^^^^^^
4-
| Problematic object instantiation: the outer and arg 1 are not fully initialized. Calling trace:
5-
| -> class N(d: D) extends M(d) { [ secondary-ctor4.scala:59 ]
6-
| ^
7-
| -> def this(d: D) = { [ secondary-ctor4.scala:7 ]
8-
| ^
9-
| -> new A(new B(new D)) // error [ secondary-ctor4.scala:42 ]
10-
| ^^^^^
11-
| -> class D { [ secondary-ctor4.scala:52 ]
12-
| ^
13-
| -> val c = new C(b, 5) // error [ secondary-ctor4.scala:54 ]
14-
| ^^^^^^^^^^^
4+
| Problematic object instantiation: the outer M.this and arg 1 are not fully initialized. Calling trace:
5+
| -> class N(d: D) extends M(d) { [ secondary-ctor4.scala:59 ]
6+
| ^
7+
| -> def this(d: D) = { [ secondary-ctor4.scala:7 ]
8+
| ^
9+
| -> new A(new B(new D)) // error [ secondary-ctor4.scala:42 ]
10+
| ^^^^^
11+
| -> class D { [ secondary-ctor4.scala:52 ]
12+
| ^
13+
| -> val c = new C(b, 5) // error [ secondary-ctor4.scala:54 ]
14+
| ^^^^^^^^^^^
1515
|
16-
| It leads to the following error during object initialization:
17-
| Access field on a value with an unknown initialization status. Calling trace:
18-
| -> def this(b: B, x: Int) = this(b) [ secondary-ctor4.scala:49 ]
19-
| ^^^^^^^
20-
| -> class C(b: B) extends A(b) with T { [ secondary-ctor4.scala:48 ]
21-
| ^
22-
| -> def this(b: B) = { [ secondary-ctor4.scala:17 ]
23-
| ^
24-
| -> Inner().foo() [ secondary-ctor4.scala:26 ]
25-
| ^^^^^^^
26-
| -> class Inner() { [ secondary-ctor4.scala:21 ]
27-
| ^
28-
| -> println(b.n) [ secondary-ctor4.scala:23 ]
29-
| ^^^
16+
| It leads to the following error during object initialization:
17+
| Access field on a value with an unknown initialization status. Calling trace:
18+
| -> def this(b: B, x: Int) = this(b) [ secondary-ctor4.scala:49 ]
19+
| ^^^^^^^
20+
| -> class C(b: B) extends A(b) with T { [ secondary-ctor4.scala:48 ]
21+
| ^
22+
| -> def this(b: B) = { [ secondary-ctor4.scala:17 ]
23+
| ^
24+
| -> Inner().foo() [ secondary-ctor4.scala:26 ]
25+
| ^^^^^^^
26+
| -> class Inner() { [ secondary-ctor4.scala:21 ]
27+
| ^
28+
| -> println(b.n) [ secondary-ctor4.scala:23 ]
29+
| ^^^
3030
-- Error: tests/init/neg/secondary-ctor4.scala:42:4 --------------------------------------------------------------------
3131
42 | new A(new B(new D)) // error
3232
| ^^^^^^^^^^^^^^^^^^^
33-
| Problematic object instantiation: the outer and arg 1 are not fully initialized. Calling trace:
33+
| Problematic object instantiation: the outer M.this and arg 1 are not fully initialized. Calling trace:
3434
| -> class N(d: D) extends M(d) { [ secondary-ctor4.scala:59 ]
3535
| ^
3636
| -> def this(d: D) = { [ secondary-ctor4.scala:7 ]

0 commit comments

Comments
 (0)