Skip to content

Commit 4518be0

Browse files
authored
Merge pull request #15263 from lampepfl/restrict-early-promote
Refine error messages for early promotion
2 parents e2b8098 + ce3c803 commit 4518be0

15 files changed

+485
-437
lines changed

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

+31-42
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,21 @@ import util.SourcePosition
99
import Decorators._, printing.SyntaxHighlighting
1010
import Types._, Symbols._, Contexts._
1111

12-
object Errors {
13-
type Errors = Seq[Error]
14-
val empty: Errors = Nil
15-
16-
def show(errs: Errors)(using Context): String =
17-
errs.map(_.show).mkString(", ")
12+
import scala.collection.mutable
1813

14+
object Errors:
1915
sealed trait Error {
20-
def source: Tree
2116
def trace: Seq[Tree]
2217
def show(using Context): String
2318

24-
def issue(using Context): Unit =
25-
report.warning(show + stacktrace, source.srcPos)
19+
def pos(using Context): SourcePosition = trace.last.sourcePos
2620

27-
def toErrors: Errors = this :: Nil
21+
def issue(using Context): Unit =
22+
report.warning(show + stacktrace, this.pos)
2823

29-
def stacktrace(using Context): String = if (trace.isEmpty) "" else " Calling trace:\n" + {
30-
var last: String = ""
31-
val sb = new StringBuilder
24+
def stacktrace(using Context): String = if trace.isEmpty then "" else " Calling trace:\n" + {
25+
var lastLineNum = -1
26+
var lines: mutable.ArrayBuffer[String] = new mutable.ArrayBuffer
3227
trace.foreach { tree =>
3328
val pos = tree.sourcePos
3429
val prefix = "-> "
@@ -44,10 +39,16 @@ object Errors {
4439
positionMarker(pos)
4540
else ""
4641

47-
if (last != line) sb.append(prefix + line + "\n" + positionMarkerLine )
42+
// always use the more precise trace location
43+
if lastLineNum == pos.line then
44+
lines.dropRightInPlace(1)
4845

49-
last = line
46+
lines += (prefix + line + "\n" + positionMarkerLine)
47+
48+
lastLineNum = pos.line
5049
}
50+
val sb = new StringBuilder
51+
for line <- lines do sb.append(line)
5152
sb.toString
5253
}
5354

@@ -65,13 +66,6 @@ object Errors {
6566
s"$padding$carets\n"
6667
}
6768

68-
/** Flatten UnsafePromotion errors
69-
*/
70-
def flatten: Errors = this match {
71-
case unsafe: UnsafePromotion => unsafe.errors.flatMap(_.flatten)
72-
case _ => this :: Nil
73-
}
74-
7569
override def toString() = this.getClass.getName.nn
7670
}
7771

@@ -81,42 +75,37 @@ object Errors {
8175
def show(using Context): String =
8276
"Access non-initialized " + field.show + "."
8377

84-
override def issue(using Context): Unit =
85-
report.warning(show + stacktrace, field.srcPos)
78+
override def pos(using Context): SourcePosition = field.sourcePos
8679
}
8780

88-
/** Promote `this` under initialization to fully-initialized */
89-
case class PromoteError(msg: String, source: Tree, trace: Seq[Tree]) extends Error {
90-
def show(using Context): String = "Cannot prove that the value is fully initialized. " + msg + "."
81+
/** Promote a value under initialization to fully-initialized */
82+
case class PromoteError(msg: String, trace: Seq[Tree]) extends Error {
83+
def show(using Context): String = msg
9184
}
9285

93-
case class AccessCold(field: Symbol, source: Tree, trace: Seq[Tree]) extends Error {
86+
case class AccessCold(field: Symbol, trace: Seq[Tree]) extends Error {
9487
def show(using Context): String =
95-
"Access field " + source.show + " on a value with an unknown initialization status."
88+
"Access field on a value with an unknown initialization status."
9689
}
9790

98-
case class CallCold(meth: Symbol, source: Tree, trace: Seq[Tree]) extends Error {
91+
case class CallCold(meth: Symbol, trace: Seq[Tree]) extends Error {
9992
def show(using Context): String =
100-
"Call method " + source.show + " on a value with an unknown initialization" + "."
93+
"Call method on a value with an unknown initialization" + "."
10194
}
10295

103-
case class CallUnknown(meth: Symbol, source: Tree, trace: Seq[Tree]) extends Error {
96+
case class CallUnknown(meth: Symbol, trace: Seq[Tree]) extends Error {
10497
def show(using Context): String =
10598
val prefix = if meth.is(Flags.Method) then "Calling the external method " else "Accessing the external field"
10699
prefix + meth.show + " may cause initialization errors" + "."
107100
}
108101

109102
/** Promote a value under initialization to fully-initialized */
110-
case class UnsafePromotion(msg: String, source: Tree, trace: Seq[Tree], errors: Errors) extends Error {
111-
assert(errors.nonEmpty)
103+
case class UnsafePromotion(msg: String, trace: Seq[Tree], error: Error) extends Error {
112104
override def issue(using Context): Unit =
113-
report.warning(show, source.srcPos)
105+
report.warning(show, this.pos)
114106

115-
def show(using Context): String = {
116-
var index = 0
117-
"Cannot prove that the value is fully initialized. " + msg + ".\n" + stacktrace +
118-
"\nThe unsafe promotion may cause the following problem:\n" +
119-
errors.head.show + errors.head.stacktrace
120-
}
107+
def show(using Context): String =
108+
msg + stacktrace + "\n" +
109+
"Promoting the value to fully initialized failed due to the following problem:\n" +
110+
error.show + error.stacktrace
121111
}
122-
}

0 commit comments

Comments
 (0)