Skip to content

Commit a86e388

Browse files
authored
Fallback null insertion for autoclonetype (#801)
If autoclonetype is unable to determine an outer class, this attempts to insert a null (and give a deprecation warning), preserving old behavior (in some cases) where the new behavior doesn't work. This doesn't provide full compatibility with old autoclonetype: this does not attempt null insertion in the general first argument (if it's not an outer class reference). Reasoning is that inserting a null for an explicit argument is probably not the right thing to do, and will likely cause a difficult-to-debug NullPointerException (whereas that would be unlikely for an outer class, which is not always referenced in Bundle subclass code).
1 parent f3a39af commit a86e388

File tree

3 files changed

+35
-10
lines changed

3 files changed

+35
-10
lines changed

chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,10 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
589589
case (outerClass, _) => Some(outerClass)
590590
}
591591

592+
// For compatibility with pre-3.1, where null is tried as an argument to the constructor.
593+
// This stores potential error messages which may be used later.
594+
var outerClassError: Option[String] = None
595+
592596
// Check if this is an inner class, and if so, try to get the outer instance
593597
val outerClassInstance = enclosingClassOption.map { outerClass =>
594598
def canAssignOuterClass(x: Object) = outerClass.isAssignableFrom(x.getClass)
@@ -612,10 +616,14 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
612616
case outer :: Nil =>
613617
_outerInst = Some(outer) // record the guess for future use
614618
outer
615-
case Nil => autoClonetypeError(s"Unable to determine instance of outer class $outerClass," +
616-
s" no candidates assignable to outer class types; examined $allOuterCandidates")
617-
case candidates => autoClonetypeError(s"Unable to determine instance of outer class $outerClass," +
618-
s" multiple possible candidates $candidates assignable to outer class type")
619+
case Nil => // TODO: replace with fatal autoClonetypeError once compatibility period is dropped
620+
outerClassError = Some(s"Unable to determine instance of outer class $outerClass," +
621+
s" no candidates assignable to outer class types; examined $allOuterCandidates")
622+
null
623+
case candidates => // TODO: replace with fatal autoClonetypeError once compatibility period is dropped
624+
outerClassError = Some(s"Unable to determine instance of outer class $outerClass," +
625+
s" multiple possible candidates $candidates assignable to outer class type")
626+
null
619627
}
620628
}
621629
}
@@ -635,7 +643,12 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
635643
case (Nil, None) => // no arguments, no outer class, invoke constructor directly
636644
Some(ctor.newInstance().asInstanceOf[this.type])
637645
case (argType :: Nil, Some((_, outerInstance))) =>
638-
if (argType isAssignableFrom outerInstance.getClass) {
646+
if (outerInstance == null) {
647+
Builder.deprecated(s"chisel3.1 autoclonetype failed, falling back to 3.0 behavior using null as the outer instance." +
648+
s" Autoclonetype failure reason: ${outerClassError.get}",
649+
Some(s"$clazz"))
650+
Some(ctor.newInstance(outerInstance).asInstanceOf[this.type])
651+
} else if (argType isAssignableFrom outerInstance.getClass) {
639652
Some(ctor.newInstance(outerInstance).asInstanceOf[this.type])
640653
} else {
641654
None
@@ -738,6 +751,7 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
738751

739752
// Invoke ctor
740753
val classMirror = outerClassInstance match {
754+
case Some((_, null)) => autoClonetypeError(outerClassError.get) // deals with the null hack for 3.0 compatibility
741755
case Some((_, outerInstance)) => mirror.reflect(outerInstance).reflectClass(classSymbol)
742756
case _ => mirror.reflectClass(classSymbol)
743757
}

src/test/scala/chiselTests/AutoClonetypeSpec.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,15 @@ class AutoClonetypeSpec extends ChiselFlatSpec {
161161
} }
162162
}
163163

164+
"3.0 null compatibility" should "not need clonetype" in {
165+
elaborate { new Module {
166+
class InnerClassThing {
167+
def createBundle = new Bundle {
168+
val a = Output(UInt(8.W))
169+
}
170+
}
171+
val io = IO((new InnerClassThing).createBundle)
172+
val a = WireInit(io)
173+
} }
174+
}
164175
}

src/test/scala/chiselTests/AutoNestedCloneSpec.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers {
102102
}
103103
}
104104

105-
behavior of "anonymous doubly-nested inner bundle fails with clear error"
106-
( the[ChiselException] thrownBy {
107-
elaborate {
105+
// Test ignored because the compatibility null-inserting autoclonetype doesn't trip this
106+
ignore should "fail on anonymous doubly-nested inner bundle with clear error" in {
107+
intercept[ChiselException] { elaborate {
108108
class Outer(val w: Int) extends Module {
109109
class Middle(val w: Int) {
110110
def getIO = new Bundle {
@@ -115,6 +115,6 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers {
115115
val myWire = Wire((new Middle(w)).getIO)
116116
}
117117
new Outer(2)
118-
}
119-
}).getMessage should include("Unable to determine instance")
118+
}}.getMessage should include("Unable to determine instance")
119+
}
120120
}

0 commit comments

Comments
 (0)