Skip to content

Commit

Permalink
Refine marker positions
Browse files Browse the repository at this point in the history
  • Loading branch information
liufengyun committed May 22, 2022
1 parent 320efe8 commit b9d3145
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 64 deletions.
42 changes: 18 additions & 24 deletions compiler/src/dotty/tools/dotc/transform/init/Errors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@ import util.SourcePosition
import Decorators._, printing.SyntaxHighlighting
import Types._, Symbols._, Contexts._

object Errors {
type Errors = Seq[Error]
val empty: Errors = Nil

def show(errs: Errors)(using Context): String =
errs.map(_.show).mkString(", ")
import scala.collection.mutable

object Errors:
sealed trait Error {
def source: Tree
def trace: Seq[Tree]
Expand All @@ -24,11 +20,12 @@ object Errors {
def issue(using Context): Unit =
report.warning(show + stacktrace, source.srcPos)

def toErrors: Errors = this :: Nil
private def isTraceInformative: Boolean =
trace.size > 1 || trace.size == 1 && trace.head.ne(source)

def stacktrace(using Context): String = if (trace.isEmpty) "" else " Calling trace:\n" + {
var last: String = ""
val sb = new StringBuilder
def stacktrace(using Context): String = if !isTraceInformative then "" else " Calling trace:\n" + {
var lastLineNum = -1
var lines: mutable.ArrayBuffer[String] = new mutable.ArrayBuffer
trace.foreach { tree =>
val pos = tree.sourcePos
val prefix = "-> "
Expand All @@ -44,10 +41,16 @@ object Errors {
positionMarker(pos)
else ""

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

last = line
lines += (prefix + line + "\n" + positionMarkerLine)

lastLineNum = pos.line
}
val sb = new StringBuilder
for line <- lines do sb.append(line)
sb.toString
}

Expand All @@ -65,13 +68,6 @@ object Errors {
s"$padding$carets\n"
}

/** Flatten UnsafePromotion errors
*/
def flatten: Errors = this match {
case unsafe: UnsafePromotion => unsafe.errors.flatMap(_.flatten)
case _ => this :: Nil
}

override def toString() = this.getClass.getName.nn
}

Expand Down Expand Up @@ -107,16 +103,14 @@ object Errors {
}

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

def show(using Context): String = {
var index = 0
msg + "\n" + stacktrace + "\n" +
msg + stacktrace + "\n" +
"Promoting the value to fully initialized failed due to the following problem:\n" +
errors.head.show + errors.head.stacktrace
error.show + error.stacktrace
}
}
}
58 changes: 30 additions & 28 deletions compiler/src/dotty/tools/dotc/transform/init/Semantic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -928,38 +928,41 @@ object Semantic {
extension (value: Value)
/** Promotion of values to hot */
def promote(msg: String, source: Tree): Contextual[Unit] = log("promoting " + value + ", promoted = " + promoted, printer) {
val trace2 = trace.add(source)
if promoted.isCurrentObjectPromoted then Nil else
given Trace = trace2

value.match
case Hot =>
value.match
case Hot =>

case Cold =>
reporter.report(PromoteError(msg, source, trace.toVector))

case thisRef: ThisRef =>
if !thisRef.tryPromoteCurrentObject() then
case Cold =>
reporter.report(PromoteError(msg, source, trace.toVector))

case warm: Warm =>
if !promoted.contains(warm) then
promoted.add(warm)
val errors = warm.tryPromote(msg, source)
if errors.nonEmpty then promoted.remove(warm)
for error <- errors do reporter.report(error)

case fun @ Fun(body, thisV, klass, env) =>
if !promoted.contains(fun) then
val errors = Reporter.stopEarly {
val res = withEnv(env) { eval(body, thisV, klass) }
res.promote("The function return value is not fully initialized.", source)
}
if (errors.nonEmpty)
reporter.report(UnsafePromotion(msg, source, trace.toVector, errors))
else
promoted.add(fun)
case thisRef: ThisRef =>
if !thisRef.tryPromoteCurrentObject() then
reporter.report(PromoteError(msg, source, trace.toVector))

case warm: Warm =>
if !promoted.contains(warm) then
promoted.add(warm)
val errors = warm.tryPromote(msg, source)
if errors.nonEmpty then promoted.remove(warm)
for error <- errors do reporter.report(error)

case fun @ Fun(body, thisV, klass, env) =>
if !promoted.contains(fun) then
val errors = Reporter.stopEarly {
given Trace = Trace.empty.add(body)
val res = withEnv(env) { eval(body, thisV, klass) }
res.promote("The function return value is not fully initialized.", body)
}
if (errors.nonEmpty)
reporter.report(UnsafePromotion(msg, source, trace.toVector, errors.head))
else
promoted.add(fun)

case RefSet(refs) =>
refs.foreach(_.promote(msg, source))
case RefSet(refs) =>
refs.foreach(_.promote(msg, source))
}
end extension

Expand Down Expand Up @@ -1005,7 +1008,7 @@ object Semantic {
}

if errors.isEmpty then Nil
else UnsafePromotion(msg, source, trace.toVector, errors) :: Nil
else UnsafePromotion(msg, source, trace.toVector, errors.head) :: Nil
}

end extension
Expand Down Expand Up @@ -1281,7 +1284,6 @@ object Semantic {

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

case ddef : DefDef =>
Expand Down
5 changes: 3 additions & 2 deletions tests/init/neg/closureLeak.check
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
11 | l.foreach(a => a.addX(this)) // error
| ^^^^^^^^^^^^^^^^^
| Cannot prove the argument is fully initialized.
|
| Promoting the value to fully initialized failed due to the following problem:
| Cannot prove the argument is fully initialized.
| Cannot prove the argument is fully initialized. Calling trace:
| -> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ]
| ^^^^
2 changes: 1 addition & 1 deletion tests/init/neg/default-this.check
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
| -> val result = updateThenCompare(5) [ default-this.scala:11 ]
| ^^^^^^^^^^^^^^^^^^^^
| -> compare() // error [ default-this.scala:9 ]
| ^^^^^^^^^
| ^^^^^^^
5 changes: 3 additions & 2 deletions tests/init/neg/inherit-non-hot.check
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
-- Error: tests/init/neg/inherit-non-hot.scala:6:34 --------------------------------------------------------------------
6 | if b == null then b = new B(this) // error
| ^^^^^^^^^^^
| May only assign fully initialized value.
| Calling trace:
| May only assign fully initialized value. Calling trace:
| -> val c = new C [ inherit-non-hot.scala:19 ]
| ^^^^^
| -> class C extends A { [ inherit-non-hot.scala:15 ]
| ^
| -> val bAgain = toB.getBAgain [ inherit-non-hot.scala:16 ]
| ^^^
| -> if b == null then b = new B(this) // error [ inherit-non-hot.scala:6 ]
| ^^^^^^^^^^^
|
| Promoting the value to fully initialized failed due to the following problem:
| Cannot prove that the field val a is fully initialized. Calling trace:
Expand Down
8 changes: 5 additions & 3 deletions tests/init/neg/inlined-method.check
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
-- Error: tests/init/neg/inlined-method.scala:8:45 ---------------------------------------------------------------------
8 | scala.runtime.Scala3RunTime.assertFailed(message) // error
| ^^^^^^^
| Cannot prove the argument is fully initialized. Calling trace:
| -> Assertion.failAssert(this) [ inlined-method.scala:2 ]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| Cannot prove the argument is fully initialized. Calling trace:
| -> Assertion.failAssert(this) [ inlined-method.scala:2 ]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| -> scala.runtime.Scala3RunTime.assertFailed(message) // error [ inlined-method.scala:8 ]
| ^^^^^^^
4 changes: 3 additions & 1 deletion tests/init/neg/local-warm4.check
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
| -> val b = new B(y) [ local-warm4.scala:10 ]
| ^^^^^^^^
| -> class B(x: Int) extends A(x) { [ local-warm4.scala:13 ]
| ^
| ^^^^
| -> class A(x: Int) extends Foo(x) { [ local-warm4.scala:6 ]
| ^
| -> increment() [ local-warm4.scala:9 ]
| ^^^^^^^^^^^
| -> updateA() [ local-warm4.scala:21 ]
| ^^^^^^^^^
| -> a = newA // error [ local-warm4.scala:18 ]
| ^^^^
1 change: 0 additions & 1 deletion tests/init/neg/promotion-loop.check
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
16 | println(b) // error
| ^
| Cannot prove the argument is fully initialized.
|
| Promoting the value to fully initialized failed due to the following problem:
| Cannot prove that the field val outer is fully initialized. Calling trace:
| -> println(b) // error [ promotion-loop.scala:16 ]
Expand Down
2 changes: 0 additions & 2 deletions tests/init/neg/t3273.check
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
4 | val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error
| ^^^^^^^^^^^^^^^
| Cannot prove the argument is fully initialized.
|
| Promoting the value to fully initialized failed due to the following problem:
| Access non-initialized value num1. Calling trace:
| -> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ]
Expand All @@ -11,7 +10,6 @@
5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| Cannot prove the argument is fully initialized.
|
| Promoting the value to fully initialized failed due to the following problem:
| Access non-initialized value num2. Calling trace:
| -> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ]
Expand Down

0 comments on commit b9d3145

Please sign in to comment.