Skip to content

Commit

Permalink
Report source locator in when scoping error messages (backport #3804) (
Browse files Browse the repository at this point in the history
…#3807)

* Report source locator in when scoping error messages (#3804)

(cherry picked from commit d21a663)

# Conflicts:
#	core/src/main/scala/chisel3/Data.scala

* resolve backport conflicts

---------

Co-authored-by: Jack Koenig <[email protected]>
  • Loading branch information
mergify[bot] and jackkoenig authored Feb 1, 2024
1 parent 4515965 commit 4a34552
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 19 deletions.
8 changes: 6 additions & 2 deletions core/src/main/scala/chisel3/Data.scala
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,12 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
case _ =>
throwException(s"operand '$this' is not visible from the current module")
}
if (!MonoConnect.checkWhenVisibility(this)) {
throwException(s"operand has escaped the scope of the when in which it was constructed")
MonoConnect.checkWhenVisibility(this) match {
case Some(sourceInfo) =>
throwException(
s"operand '$this' has escaped the scope of the when (${sourceInfo.makeMessage(x => x)}) in which it was constructed."
)
case None => ()
}
}

Expand Down
4 changes: 3 additions & 1 deletion core/src/main/scala/chisel3/When.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ object when {
* added by preprocessing the command queue.
*/
final class WhenContext private[chisel3] (
sourceInfo: SourceInfo,
_sourceInfo: SourceInfo,
cond: Option[() => Bool],
block: => Any,
firrtlDepth: Int,
Expand All @@ -80,6 +80,8 @@ final class WhenContext private[chisel3] (

private var scopeOpen = false

private[chisel3] def sourceInfo: SourceInfo = _sourceInfo

/** Returns the local condition, inverted for an otherwise */
private[chisel3] def localCond: Bool = {
implicit val sourceInfo = UnlocatableSourceInfo
Expand Down
46 changes: 30 additions & 16 deletions core/src/main/scala/chisel3/internal/MonoConnect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,14 @@ private[chisel3] object MonoConnect {
MonoConnectException(
s"""${formatName(sink)} cannot be written from module ${source.parentNameOpt.getOrElse("(unknown)")}."""
)
def SourceEscapedWhenScopeException(source: Data) =
MonoConnectException(s"Source ${formatName(source)} has escaped the scope of the when in which it was constructed.")
def SinkEscapedWhenScopeException(sink: Data) =
MonoConnectException(s"Sink ${formatName(sink)} has escaped the scope of the when in which it was constructed.")
def SourceEscapedWhenScopeException(source: Data, whenInfo: SourceInfo) =
MonoConnectException(
s"Source ${formatName(source)} has escaped the scope of the when (${whenInfo.makeMessage(x => x)}) in which it was constructed."
)
def SinkEscapedWhenScopeException(sink: Data, whenInfo: SourceInfo) =
MonoConnectException(
s"Sink ${formatName(sink)} has escaped the scope of the when (${whenInfo.makeMessage(x => x)}) in which it was constructed."
)
def UnknownRelationException =
MonoConnectException("Sink or source unavailable to current module.")
// These are when recursing down aggregate types
Expand All @@ -74,12 +78,18 @@ private[chisel3] object MonoConnect {
s"Source ${formatName(source)} and sink ${formatName(sink)} of type Analog cannot participate in a mono connection (:=)"
)

def checkWhenVisibility(x: Data): Boolean = {
/** Check if the argument is visible from current when scope
*
* Returns source locator of original when declaration if not visible (for error reporting)
*
* @return None if visible, Some(location of original when declaration)
*/
def checkWhenVisibility(x: Data): Option[SourceInfo] = {
x.topBinding match {
case mp: MemoryPortBinding =>
true // TODO (albert-magyar): remove this "bridge" for odd enable logic of current CHIRRTL memories
case cd: ConditionalDeclarable => cd.visibility.map(_.active).getOrElse(true)
case _ => true
None // TODO (albert-magyar): remove this "bridge" for odd enable logic of current CHIRRTL memories
case cd: ConditionalDeclarable => cd.visibility.collect { case wc: WhenContext if !wc.active => wc.sourceInfo }
case _ => None
}
}

Expand Down Expand Up @@ -241,12 +251,14 @@ private[chisel3] object MonoConnect {
case _ => false
}

if (!checkWhenVisibility(sink)) {
throw SinkEscapedWhenScopeException(sink)
checkWhenVisibility(sink) match {
case Some(whenInfo) => throw SinkEscapedWhenScopeException(sink, whenInfo)
case None => ()
}

if (!checkWhenVisibility(source)) {
throw SourceEscapedWhenScopeException(source)
checkWhenVisibility(source) match {
case Some(whenInfo) => throw SourceEscapedWhenScopeException(source, whenInfo)
case None => ()
}

// CASE: Context is same module that both sink node and source node are in
Expand Down Expand Up @@ -393,12 +405,14 @@ private[chisel3] object MonoConnect {
val sink_direction = BindingDirection.from(sink.topBinding, sink.direction)
val source_direction = BindingDirection.from(source.topBinding, source.direction)

if (!checkWhenVisibility(sink)) {
throw SinkEscapedWhenScopeException(sink)
checkWhenVisibility(sink) match {
case Some(whenInfo) => throw SinkEscapedWhenScopeException(sink, whenInfo)
case None => ()
}

if (!checkWhenVisibility(source)) {
throw SourceEscapedWhenScopeException(source)
checkWhenVisibility(source) match {
case Some(whenInfo) => throw SourceEscapedWhenScopeException(source, whenInfo)
case None => ()
}

// CASE: Context is same module that both left node and right node are in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import circt.stage.ChiselStage
import chisel3._
import chisel3.testers.BasicTester
import chisel3.util._
import chisel3.experimental.{SourceInfo, SourceLine}

class WhenTester() extends BasicTester {
val cnt = Counter(4)
Expand Down Expand Up @@ -165,4 +166,42 @@ class WhenSpec extends ChiselFlatSpec with Utils {
}
e.getMessage should include("Cannot exit from a when() block with a \"return\"")
}

"Using a value that has escaped from a when scope in a connection" should "give a reasonable error message" in {
implicit val info: SourceInfo = SourceLine("Foo.scala", 12, 3)
val e = the[ChiselException] thrownBy {
ChiselStage.emitCHIRRTL(new Module {
override def desiredName = "Top"
val foo, bar = IO(Output(UInt(8.W)))
val a = IO(Input(Bool()))
lazy val w = Wire(UInt(8.W))
when(a) {
foo := w
}
bar := w
})
}
val msg =
"Source foo_w in Top has escaped the scope of the when (@[Foo.scala 12:3]) in which it was constructed."
e.getMessage should include(msg)
}

"Using a value that has escaped from a when scope in an operation" should "give a reasonable error message" in {
implicit val info: SourceInfo = SourceLine("Foo.scala", 12, 3)
val e = the[ChiselException] thrownBy {
ChiselStage.emitCHIRRTL(new Module {
override def desiredName = "Top"
val foo, bar = IO(Output(UInt(8.W)))
val a = IO(Input(Bool()))
lazy val w = Wire(UInt(8.W))
when(a) {
foo := w
}
bar := w + 1.U
})
}
val msg =
"operand 'Top.foo_w: Wire[UInt<8>]' has escaped the scope of the when (@[Foo.scala 12:3]) in which it was constructed."
e.getMessage should include(msg)
}
}

0 comments on commit 4a34552

Please sign in to comment.