diff options
3 files changed, 51 insertions, 11 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala index 43e430cd..14011cd9 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala @@ -556,6 +556,11 @@ class Bundle(implicit compileOptions: CompileOptions) extends Record { case _ => None } + // If this Data is an instance of an inner class, record a *guess* at the outer object. + // This is only used for cloneType, and is mutable to allow autoCloneType to try guesses other + // than the module that was current when the Bundle was constructed. + private var _outerInst: Option[Object] = Builder.currentModule + override def cloneType : this.type = { // This attempts to infer constructor and arguments to clone this Bundle subtype without // requiring the user explicitly overriding cloneType. @@ -570,16 +575,22 @@ class Bundle(implicit compileOptions: CompileOptions) extends Record { // Check if this is an inner class, and if so, try to get the outer instance val clazz = this.getClass val outerClassInstance = Option(clazz.getEnclosingClass).map { outerClass => + def canAssignOuterClass(x: Object) = outerClass.isAssignableFrom(x.getClass) val outerInstance = try { clazz.getDeclaredField("$outer").get(this) // doesn't work in all cases, namely anonymous inner Bundles } catch { case _: NoSuchFieldException => - this.outerModule.getOrElse(reflectError(s"Unable to determine instance of outer class $outerClass")) + // First check if this Bundle is bound and an anonymous inner Bundle of another Bundle + this.bindingOpt.collect { case ChildBinding(p) if canAssignOuterClass(p) => p } + .orElse(this._outerInst) + .getOrElse(reflectError(s"Unable to determine instance of outer class $outerClass")) } - if (!outerClass.isAssignableFrom(outerInstance.getClass)) { + if (!canAssignOuterClass(outerInstance)) { reflectError(s"Unable to determine instance of outer class $outerClass," + s" guessed $outerInstance, but is not assignable to outer class $outerClass") } + // Record the outer instance for future cloning + this._outerInst = Some(outerInstance) (outerClass, outerInstance) } @@ -605,7 +616,7 @@ class Bundle(implicit compileOptions: CompileOptions) extends Record { } clone match { case Some(clone) => - clone.outerModule = this.outerModule + clone._outerInst = this._outerInst if (!clone.typeEquivalent(this)) { reflectError(s"Automatically cloned $clone not type-equivalent to base $this." + " Constructor argument values were not inferred, ensure constructor is deterministic.") @@ -650,7 +661,7 @@ class Bundle(implicit compileOptions: CompileOptions) extends Record { require(ctors.size == 1) // should be consistent with Scala constructors try { val clone = ctors.head.newInstance(outerClassInstance.get._2).asInstanceOf[this.type] - clone.outerModule = this.outerModule + clone._outerInst = this._outerInst return clone } catch { case e @ (_: java.lang.reflect.InvocationTargetException | _: IllegalArgumentException) => @@ -703,7 +714,7 @@ class Bundle(implicit compileOptions: CompileOptions) extends Record { case None => mirror.reflectClass(classSymbol) } val clone = classMirror.reflectConstructor(ctor).apply(ctorParamsVals:_*).asInstanceOf[this.type] - clone.outerModule = this.outerModule + clone._outerInst = this._outerInst if (!clone.typeEquivalent(this)) { reflectError(s"Automatically cloned $clone not type-equivalent to base $this." + diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala index 35401fa1..1cf50e9f 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala @@ -208,11 +208,6 @@ abstract class Data extends HasId { } } - // If this Data is an instance of an inner class, record enclosing class - // This is only used for cloneType, and is mutable to allow autoCloneType to preserve the - // original outerModule. - private[core] var outerModule: Option[BaseModule] = Builder.currentModule - // User-specified direction, local at this node only. // Note that the actual direction of this node can differ from child and parent specifiedDirection. private var _specifiedDirection: SpecifiedDirection = SpecifiedDirection.Unspecified @@ -245,6 +240,7 @@ abstract class Data extends HasId { // This information is supplemental (more than is necessary to generate FIRRTL) and is used to // perform checks in Chisel, where more informative error messages are possible. private var _binding: Option[Binding] = None + private[core] def bindingOpt = _binding private[core] def hasBinding = _binding.isDefined // Only valid after node is bound (synthesizable), crashes otherwise private[core] def binding = _binding.get diff --git a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala index d3977213..746780be 100644 --- a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala +++ b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala @@ -5,6 +5,12 @@ import Chisel.ChiselException import org.scalatest._ import chisel3._ +class BundleWithAnonymousInner(val w: Int) extends Bundle { + val inner = new Bundle { + val foo = Input(UInt(w.W)) + } +} + class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers { behavior of "autoCloneType of inner Bundle in Chisel3" @@ -18,7 +24,7 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers { def getIO = new InnerIOType } val io = IO(new Bundle {}) - val myWire = Wire((new Middle(w)).getIO) + val myWire = Wire((new Middle(w)).getIO) } new Outer(2) } @@ -56,6 +62,33 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers { } } + it should "clone an anonymous, bound, inner bundle of another bundle successfully" in { + elaborate { + class TestModule(w: Int) extends Module { + val io = IO(new BundleWithAnonymousInner(w) ) + val w0 = WireInit(io) + val w1 = WireInit(io.inner) + } + new TestModule(8) + } + } + + it should "clone an anonymous, inner bundle of a Module, bound to another bundle successfully" in { + elaborate { + class TestModule(w: Int) extends Module { + val bun = new Bundle { + val foo = UInt(w.W) + } + val io = IO(new Bundle { + val inner = Input(bun) + }) + val w0 = WireInit(io) + val w1 = WireInit(io.inner) + } + new TestModule(8) + } + } + behavior of "anonymous doubly-nested inner bundle fails with clear error" ( the[ChiselException] thrownBy { elaborate { |
