summaryrefslogtreecommitdiff
path: root/chiselFrontend/src/main/scala/chisel3/core
diff options
context:
space:
mode:
authorRichard Lin2018-02-07 12:04:09 -0800
committerGitHub2018-02-07 12:04:09 -0800
commit7be9b1c681558695b95fccb22a60c34101c86118 (patch)
treeeb0eaef935b55ace66ae49bdce0b3428fcc09c5c /chiselFrontend/src/main/scala/chisel3/core
parent1bfca502c69a26edca86d716a1ca9d24e6789e59 (diff)
Better support for autoclonetype of nested Bundles (#769)
* Better support for autoclonetype of nested Bundles * Move bundleStack to dynamicContext * prefer $outer if available, make guesses distinct * Catch IllegalAccessException in autoclonetype In strange circumstances this type of exception can occur when accessing $outer
Diffstat (limited to 'chiselFrontend/src/main/scala/chisel3/core')
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala54
1 files changed, 36 insertions, 18 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
index 6a2e68e0..fa69bef0 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
@@ -552,10 +552,15 @@ 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
+ // Memoize the outer instance for autoclonetype, especially where this is context-dependent
+ // (like the outer module or enclosing Bundles).
+ private var _outerInst: Option[Object] = None
+
+ // For autoclonetype, record possible candidates for outer instance.
+ // _outerInst should always take precedence, since it should be propagated from the original
+ // object which has the most accurate context.
+ private val _containingModule: Option[BaseModule] = Builder.currentModule
+ private val _containingBundles: Seq[Bundle] = Builder.updateBundleStack(this)
override def cloneType : this.type = {
// This attempts to infer constructor and arguments to clone this Bundle subtype without
@@ -570,23 +575,36 @@ 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 =>
- // 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 (!canAssignOuterClass(outerInstance)) {
- reflectError(s"Unable to determine instance of outer class $outerClass," +
- s" guessed $outerInstance, but is not assignable to outer class $outerClass")
+
+ val outerInstance = _outerInst match {
+ case Some(outerInstance) => outerInstance // use _outerInst if defined
+ case None => // determine outer instance if not already recorded
+ try {
+ // Prefer this if it works, but doesn't work in all cases, namely anonymous inner Bundles
+ val outer = clazz.getDeclaredField("$outer").get(this)
+ _outerInst = Some(outer)
+ outer
+ } catch {
+ case (_: NoSuchFieldException | _: IllegalAccessException) =>
+ // Fallback using guesses based on common patterns
+ val allOuterCandidates = Seq(
+ _containingModule.toSeq,
+ _containingBundles
+ ).flatten.distinct
+ allOuterCandidates.filter(canAssignOuterClass(_)) match {
+ case outer :: Nil =>
+ _outerInst = Some(outer) // record the guess for future use
+ outer
+ case Nil => reflectError(s"Unable to determine instance of outer class $outerClass," +
+ s" no candidates assignable to outer class types; examined $allOuterCandidates")
+ case candidates => reflectError(s"Unable to determine instance of outer class $outerClass," +
+ s" multiple possible candidates $candidates assignable to outer class type")
+ }
+ }
}
- // Record the outer instance for future cloning
- this._outerInst = Some(outerInstance)
(outerClass, outerInstance)
}