Skip to content

[Bug] Argument-less .resize on a sub-design instance's OUT port crashes elaboration with NoSuchElementException in MutableDB #470

Description

@soronpo

Summary

Calling the width-inferring, argument-less form of .resize on a sub-design instance's OUT port aborts elaboration with an internal java.util.NoSuchElementException instead of either working or producing a diagnostic. The same expression with an explicit width (.resize(N)) works, and the same argument-less form on a locally declared VAR works, so the failure is specific to the combination.

Minimal reproduction

Two files. Child.scala:

import dfhdl.*
class Child(
    val A: Int <> CONST = 3,
    val B: Int <> CONST = 8
) extends EDDesign:
  val OUT_W = A * B
  val o = Bits(OUT_W) <> OUT
  o <> all(0)
end Child

Probe.scala:

import dfhdl.*
class Probe extends EDDesign:
  val p = Bits(16) <> OUT
  val c = new Child()
  p <> c.o.resize
end Probe

Run with:

scala run src/ --scala 3.8.4 \
  --dep "io.github.dfianthdl::dfhdl::$V" \
  --compiler-plugin "io.github.dfianthdl:::dfhdl-plugin::$V" \
  -O -deprecation -M Probe -- commit

Scala compilation succeeds; the failure is at elaboration.

Actual behavior

Exception in thread "main" java.util.NoSuchElementException: key not found: Dcl(DFBits("TR_b2c48732_0_30"),OUT,List(),"OW_b2c48732_0_31",Meta(Some(o),src/Child.scala:7:11 - 7:29,None,List()),Map())
	at scala.collection.MapOps.default(Map.scala:310)
	at scala.collection.MapOps.default$(Map.scala:104)
	at scala.collection.AbstractMap.default(Map.scala:464)
	at scala.collection.mutable.HashMap.apply(HashMap.scala:446)
	at dfhdl.core.DesignContext.setMember(MutableDB.scala:165)
	at dfhdl.core.MutableDB.setMember(MutableDB.scala:977)
	at dfhdl.core.MutableDB$getSet$.set(MutableDB.scala:1236)
	at dfhdl.compiler.ir.DFMember.setTags(DFMember.scala:22)
	at dfhdl.compiler.ir.DFMember.setTags$(DFMember.scala:10)
	at dfhdl.compiler.ir.DFVal$Dcl.setTags(DFMember.scala:727)
	at dfhdl.core.DFVal$.tag(DFVal.scala:442)
	at dfhdl.core.DFBits$package$DFBits$Val$Ops$.resize(DFBits.scala:746)
	at dfhdl.hdl$.resize(hdl.scala:7)
	at dfhdl.hdl$package$.resize(hdl.scala:120)
	at Probe.<init>(Probe.scala:5)
	at Probe$$anon$3.<init>(Probe.scala:2)
	at Probe$.mkDsn$1(Probe.scala:2)
	at Probe$.main$$anonfun$1(Probe.scala:2)
	at dfhdl.internals.DiskCache$Step.task$lzyINIT1$$anonfun$4(DiskCache.scala:129)
	at factum.Task.cached$$anonfun$1(Task.scala:51)
	at factum.Evaluator$Run.compute(Evaluator.scala:199)

(trace truncated at the factum.Evaluator frame)

Note the Meta in the missing key points at the child's declaration site (src/Child.scala:7, i.e. val o = Bits(OUT_W) <> OUT), while setMember is being called in the parent's DesignContext. DFVal.tag is trying to re-tag a member that does not belong to the design context it is being set in.

Expected behavior

Either the argument-less .resize infers the target width from the connection context (as it does for a local VAR), or it reports a normal DFHDL elaboration error. An internal NoSuchElementException with a raw IR Dcl(...) dump gives the user nothing to act on: there is no source position for the offending line in the message itself, only inside the stack frames, and the printed key is compiler-internal representation rather than the code that was written.

Boundary of the bug (each verified by running it)

Variant Result
p <> c.o.resize (child instance's OUT port, no argument) crash
p <> c.o.resize(16) (child instance's OUT port, explicit width) works
p <> v.resize where val v = Bits(24) <> VAR (local VAR, no argument) works
crash variant run with --nocache crash (unchanged)
child made hierarchical (child instantiates its own sub-design) not required; a leaf child reproduces it

The --nocache row matters because the missing key's name carries a digest prefix (TR_b2c48732_0_30), which initially looked like a sub-design elaboration cache interaction. It is not: the crash reproduces identically with caching disabled.

How it was hit in practice

Translating Mahmoud-geberty/HOG_fpga's norm_block.v (learner run hog-fpga-hist-1). The line was:

block_histograms <> cell_line_buffer.kernel.resize

where cell_line_buffer is a lin_buff instance and kernel is its Bits(OUTPUT_WIDTH) <> OUT port. Original trace, same signature:

Exception in thread "main" java.util.NoSuchElementException: key not found: Dcl(DFBits("TR_665f36f3_0_76"),OUT,List(),"OW_665f36f3_0_77",Meta(Some(kernel),src/lin_buff.scala:30:16 - 30:41,None,List()),Map())
	at dfhdl.core.DesignContext.setMember(MutableDB.scala:165)
	at dfhdl.compiler.ir.DFVal$Dcl.setTags(DFMember.scala:727)
	at dfhdl.core.DFBits$package$DFBits$Val$Ops$.resize(DFBits.scala:746)
	at norm_block.<init>(norm_block.scala:46)

Worth noting how the user got here: the previous attempt's own error message recommended .resize. That attempt failed with a pair of elaboration errors on the same line, reported twice with the operand roles swapped:

DFiant HDL elaboration error!
Position:  src/norm_block.scala:46:3 - 46:46
Message:   The argument width (cell_line_buffer.OUTPUT_WIDTH) is different than the receiver width (BLOCK_HIST_WIDTH).
Consider applying `.resize` to resolve this issue.

DFiant HDL elaboration error!
Position:  src/norm_block.scala:46:3 - 46:46
Message:   The argument width (BLOCK_HIST_WIDTH) is different than the receiver width (cell_line_buffer.OUTPUT_WIDTH).
Consider applying `.resize` to resolve this issue.

So the documented remedy for the width mismatch is what triggers the crash on this operand kind. That pairing is what makes this worth prioritizing over an ordinary internal-error report.

Environment

  • DFHDL: v0.22.0+63-477e737a-SNAPSHOT
  • Scala: 3.8.4, JVM 21
  • Yosys (unrelated to the crash, listed for run context): 0.68+40 (git sha1 0f2bcb94b-dirty)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions