Skip to content

Commit c1b83f6

Browse files
committed
Refine marker positions
1 parent 9a9db23 commit c1b83f6

File tree

9 files changed

+63
-64
lines changed

9 files changed

+63
-64
lines changed

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

+18-24
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,9 @@ 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 {
2016
def source: Tree
2117
def trace: Seq[Tree]
@@ -24,11 +20,12 @@ object Errors {
2420
def issue(using Context): Unit =
2521
report.warning(show + stacktrace, source.srcPos)
2622

27-
def toErrors: Errors = this :: Nil
23+
private def isTraceInformative: Boolean =
24+
trace.size > 1 || trace.size == 1 && trace.head.ne(source)
2825

29-
def stacktrace(using Context): String = if (trace.isEmpty) "" else " Calling trace:\n" + {
30-
var last: String = ""
31-
val sb = new StringBuilder
26+
def stacktrace(using Context): String = if !isTraceInformative then "" else " Calling trace:\n" + {
27+
var lastLineNum = -1
28+
var lines: mutable.ArrayBuffer[String] = new mutable.ArrayBuffer
3229
trace.foreach { tree =>
3330
val pos = tree.sourcePos
3431
val prefix = "-> "
@@ -44,10 +41,16 @@ object Errors {
4441
positionMarker(pos)
4542
else ""
4643

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

49-
last = line
48+
lines += (prefix + line + "\n" + positionMarkerLine)
49+
50+
lastLineNum = pos.line
5051
}
52+
val sb = new StringBuilder
53+
for line <- lines do sb.append(line)
5154
sb.toString
5255
}
5356

@@ -65,13 +68,6 @@ object Errors {
6568
s"$padding$carets\n"
6669
}
6770

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-
7571
override def toString() = this.getClass.getName.nn
7672
}
7773

@@ -107,16 +103,14 @@ object Errors {
107103
}
108104

109105
/** 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)
106+
case class UnsafePromotion(msg: String, source: Tree, trace: Seq[Tree], error: Error) extends Error {
112107
override def issue(using Context): Unit =
113108
report.warning(show, source.srcPos)
114109

115110
def show(using Context): String = {
116111
var index = 0
117-
msg + "\n" + stacktrace + "\n" +
112+
msg + stacktrace + "\n" +
118113
"Promoting the value to fully initialized failed due to the following problem:\n" +
119-
errors.head.show + errors.head.stacktrace
114+
error.show + error.stacktrace
120115
}
121116
}
122-
}

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

+30-28
Original file line numberDiff line numberDiff line change
@@ -928,38 +928,41 @@ object Semantic {
928928
extension (value: Value)
929929
/** Promotion of values to hot */
930930
def promote(msg: String, source: Tree): Contextual[Unit] = log("promoting " + value + ", promoted = " + promoted, printer) {
931+
val trace2 = trace.add(source)
931932
if promoted.isCurrentObjectPromoted then Nil else
933+
given Trace = trace2
932934

933-
value.match
934-
case Hot =>
935+
value.match
936+
case Hot =>
935937

936-
case Cold =>
937-
reporter.report(PromoteError(msg, source, trace.toVector))
938-
939-
case thisRef: ThisRef =>
940-
if !thisRef.tryPromoteCurrentObject() then
938+
case Cold =>
941939
reporter.report(PromoteError(msg, source, trace.toVector))
942940

943-
case warm: Warm =>
944-
if !promoted.contains(warm) then
945-
promoted.add(warm)
946-
val errors = warm.tryPromote(msg, source)
947-
if errors.nonEmpty then promoted.remove(warm)
948-
for error <- errors do reporter.report(error)
949-
950-
case fun @ Fun(body, thisV, klass, env) =>
951-
if !promoted.contains(fun) then
952-
val errors = Reporter.stopEarly {
953-
val res = withEnv(env) { eval(body, thisV, klass) }
954-
res.promote("The function return value is not fully initialized.", source)
955-
}
956-
if (errors.nonEmpty)
957-
reporter.report(UnsafePromotion(msg, source, trace.toVector, errors))
958-
else
959-
promoted.add(fun)
941+
case thisRef: ThisRef =>
942+
if !thisRef.tryPromoteCurrentObject() then
943+
reporter.report(PromoteError(msg, source, trace.toVector))
944+
945+
case warm: Warm =>
946+
if !promoted.contains(warm) then
947+
promoted.add(warm)
948+
val errors = warm.tryPromote(msg, source)
949+
if errors.nonEmpty then promoted.remove(warm)
950+
for error <- errors do reporter.report(error)
951+
952+
case fun @ Fun(body, thisV, klass, env) =>
953+
if !promoted.contains(fun) then
954+
val errors = Reporter.stopEarly {
955+
given Trace = Trace.empty.add(body)
956+
val res = withEnv(env) { eval(body, thisV, klass) }
957+
res.promote("The function return value is not fully initialized.", body)
958+
}
959+
if (errors.nonEmpty)
960+
reporter.report(UnsafePromotion(msg, source, trace.toVector, errors.head))
961+
else
962+
promoted.add(fun)
960963

961-
case RefSet(refs) =>
962-
refs.foreach(_.promote(msg, source))
964+
case RefSet(refs) =>
965+
refs.foreach(_.promote(msg, source))
963966
}
964967
end extension
965968

@@ -1005,7 +1008,7 @@ object Semantic {
10051008
}
10061009

10071010
if errors.isEmpty then Nil
1008-
else UnsafePromotion(msg, source, trace.toVector, errors) :: Nil
1011+
else UnsafePromotion(msg, source, trace.toVector, errors.head) :: Nil
10091012
}
10101013

10111014
end extension
@@ -1281,7 +1284,6 @@ object Semantic {
12811284

12821285
case vdef : ValDef =>
12831286
// local val definition
1284-
// TODO: support explicit @cold annotation for local definitions
12851287
eval(vdef.rhs, thisV, klass)
12861288

12871289
case ddef : DefDef =>

tests/init/neg/closureLeak.check

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
11 | l.foreach(a => a.addX(this)) // error
33
| ^^^^^^^^^^^^^^^^^
44
| Cannot prove the argument is fully initialized.
5-
|
65
| Promoting the value to fully initialized failed due to the following problem:
7-
| Cannot prove the argument is fully initialized.
6+
| Cannot prove the argument is fully initialized. Calling trace:
7+
| -> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ]
8+
| ^^^^

tests/init/neg/default-this.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
| -> val result = updateThenCompare(5) [ default-this.scala:11 ]
66
| ^^^^^^^^^^^^^^^^^^^^
77
| -> compare() // error [ default-this.scala:9 ]
8-
| ^^^^^^^^^
8+
| ^^^^^^^

tests/init/neg/inherit-non-hot.check

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
-- Error: tests/init/neg/inherit-non-hot.scala:6:34 --------------------------------------------------------------------
22
6 | if b == null then b = new B(this) // error
33
| ^^^^^^^^^^^
4-
| May only assign fully initialized value.
5-
| Calling trace:
4+
| May only assign fully initialized value. Calling trace:
65
| -> val c = new C [ inherit-non-hot.scala:19 ]
76
| ^^^^^
87
| -> class C extends A { [ inherit-non-hot.scala:15 ]
98
| ^
109
| -> val bAgain = toB.getBAgain [ inherit-non-hot.scala:16 ]
1110
| ^^^
11+
| -> if b == null then b = new B(this) // error [ inherit-non-hot.scala:6 ]
12+
| ^^^^^^^^^^^
1213
|
1314
| Promoting the value to fully initialized failed due to the following problem:
1415
| Cannot prove that the field val a is fully initialized. Calling trace:

tests/init/neg/inlined-method.check

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
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. Calling trace:
5-
| -> Assertion.failAssert(this) [ inlined-method.scala:2 ]
6-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
| Cannot prove the argument is fully initialized. Calling trace:
5+
| -> Assertion.failAssert(this) [ inlined-method.scala:2 ]
6+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
7+
| -> scala.runtime.Scala3RunTime.assertFailed(message) // error [ inlined-method.scala:8 ]
8+
| ^^^^^^^

tests/init/neg/local-warm4.check

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
| -> val b = new B(y) [ local-warm4.scala:10 ]
1010
| ^^^^^^^^
1111
| -> class B(x: Int) extends A(x) { [ local-warm4.scala:13 ]
12-
| ^
12+
| ^^^^
1313
| -> class A(x: Int) extends Foo(x) { [ local-warm4.scala:6 ]
1414
| ^
1515
| -> increment() [ local-warm4.scala:9 ]
1616
| ^^^^^^^^^^^
1717
| -> updateA() [ local-warm4.scala:21 ]
1818
| ^^^^^^^^^
19+
| -> a = newA // error [ local-warm4.scala:18 ]
20+
| ^^^^

tests/init/neg/promotion-loop.check

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
16 | println(b) // error
33
| ^
44
| Cannot prove the argument is fully initialized.
5-
|
65
| Promoting the value to fully initialized failed due to the following problem:
76
| Cannot prove that the field val outer is fully initialized. Calling trace:
87
| -> println(b) // error [ promotion-loop.scala:16 ]

tests/init/neg/t3273.check

-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
4 | val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error
33
| ^^^^^^^^^^^^^^^
44
| Cannot prove the argument is fully initialized.
5-
|
65
| Promoting the value to fully initialized failed due to the following problem:
76
| Access non-initialized value num1. Calling trace:
87
| -> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ]
@@ -11,7 +10,6 @@
1110
5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error
1211
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1312
| Cannot prove the argument is fully initialized.
14-
|
1513
| Promoting the value to fully initialized failed due to the following problem:
1614
| Access non-initialized value num2. Calling trace:
1715
| -> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ]

0 commit comments

Comments
 (0)