summaryrefslogtreecommitdiff
path: root/macros/src/main/scala/chisel3/internal/naming
diff options
context:
space:
mode:
authorJack Koenig2020-03-22 18:13:58 -0700
committerJack Koenig2020-03-25 19:17:15 -0700
commitfbf5e6f1a0e8bf535d465b748ad554575fe62156 (patch)
tree578858ab6d219ca6daf44cf87b73f75054989097 /macros/src/main/scala/chisel3/internal/naming
parentb2e004fb615a3c931d910a338b9faa99c1c975d7 (diff)
Rename subprojects to more canonical names
* Rename coreMacros to macros * Rename chiselFrontend to core Also make each subproject publish with "chisel3-" as a prefix
Diffstat (limited to 'macros/src/main/scala/chisel3/internal/naming')
-rw-r--r--macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala205
1 files changed, 205 insertions, 0 deletions
diff --git a/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala b/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala
new file mode 100644
index 00000000..bf4879ec
--- /dev/null
+++ b/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala
@@ -0,0 +1,205 @@
+// See LICENSE for license details.
+
+// Transform implementations for name-propagation related annotations.
+//
+// Helpful references:
+// http://docs.scala-lang.org/overviews/quasiquotes/syntax-summary.html#definitions
+// for quasiquote structures of various Scala structures
+// http://jsuereth.com/scala/2009/02/05/leveraging-annotations-in-scala.html
+// use of Transformer
+// http://www.scala-lang.org/old/sites/default/files/sids/rytz/Wed,%202010-01-27,%2015:10/annots.pdf
+// general annotations reference
+
+package chisel3.internal.naming
+
+import scala.reflect.macros.whitebox.Context
+import scala.annotation.{StaticAnnotation, compileTimeOnly}
+import scala.language.experimental.macros
+
+// Workaround for https://github.com/sbt/sbt/issues/3966
+object DebugTransforms
+class DebugTransforms(val c: Context) {
+ import c.universe._
+
+ /** Passthrough transform that prints the annottee for debugging purposes.
+ * No guarantees are made on what this annotation does, and it may very well change over time.
+ *
+ * The print is warning level to make it visually easier to spot, as well as a reminder that
+ * this annotation should not make it to production / committed code.
+ */
+ def dump(annottees: c.Tree*): c.Tree = {
+ val combined = annottees.map({ tree => show(tree) }).mkString("\r\n\r\n")
+ annottees.foreach(tree => c.warning(c.enclosingPosition, s"Debug dump:\n$combined"))
+ q"..$annottees"
+ }
+
+ /** Passthrough transform that prints the annottee as a tree for debugging purposes.
+ * No guarantees are made on what this annotation does, and it may very well change over time.
+ *
+ * The print is warning level to make it visually easier to spot, as well as a reminder that
+ * this annotation should not make it to production / committed code.
+ */
+ def treedump(annottees: c.Tree*): c.Tree = {
+ val combined = annottees.map({ tree => showRaw(tree) }).mkString("\r\n")
+ annottees.foreach(tree => c.warning(c.enclosingPosition, s"Debug tree dump:\n$combined"))
+ q"..$annottees"
+ }
+}
+
+// Workaround for https://github.com/sbt/sbt/issues/3966
+object NamingTransforms
+class NamingTransforms(val c: Context) {
+ import c.universe._
+
+ val globalNamingStack = q"_root_.chisel3.internal.DynamicNamingStack"
+
+ /** Base transformer that provides the val name transform.
+ * Should not be instantiated, since by default this will recurse everywhere and break the
+ * naming context variable bounds.
+ */
+ trait ValNameTransformer extends Transformer {
+ val contextVar: TermName
+
+ override def transform(tree: Tree): Tree = tree match {
+ // Intentionally not prefixed with $mods, since modifiers usually mean the val definition
+ // is in a non-transformable location, like as a parameter list.
+ // TODO: is this exhaustive / correct in all cases?
+ case q"val $tname: $tpt = $expr" => {
+ val TermName(tnameStr: String) = tname
+ val transformedExpr = super.transform(expr)
+ q"val $tname: $tpt = $contextVar.name($transformedExpr, $tnameStr)"
+ }
+ case other => super.transform(other)
+ }
+ }
+
+ /** Module-specific val name transform, containing logic to prevent from recursing into inner
+ * classes and applies the naming transform on inner functions.
+ */
+ class ClassBodyTransformer(val contextVar: TermName) extends ValNameTransformer {
+ override def transform(tree: Tree): Tree = tree match {
+ case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" => // scalastyle:ignore line.size.limit
+ tree // don't recurse into inner classes
+ case q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }" =>
+ tree // don't recurse into inner classes
+ case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => {
+ val Modifiers(_, _, annotations) = mods
+ // don't apply naming transform twice
+ val containsChiselName = annotations.map({q"new chiselName()" equalsStructure _}).fold(false)({_||_})
+ // transforming overloaded initializers causes errors, and the transform isn't helpful
+ val isInitializer = tname == TermName("<init>")
+ if (containsChiselName || isInitializer) {
+ tree
+ } else {
+ // apply chiselName transform by default
+ val transformedExpr = transformHierarchicalMethod(expr)
+ q"$mods def $tname[..$tparams](...$paramss): $tpt = $transformedExpr"
+ }
+ }
+ case other => super.transform(other)
+ }
+ }
+
+ /** Method-specific val name transform, handling the return case.
+ */
+ class MethodTransformer(val contextVar: TermName) extends ValNameTransformer {
+ override def transform(tree: Tree): Tree = tree match {
+ // TODO: better error messages when returning nothing
+ case q"return $expr" => q"return $globalNamingStack.popReturnContext($expr, $contextVar)"
+ // Do not recurse into methods
+ case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => tree
+ case other => super.transform(other)
+ }
+ }
+
+ /** Applies the val name transform to a class body.
+ * Closes context on top level or return local context to englobing context.
+ * Closing context only makes sense when top level a Module.
+ * A Module is always the naming top level.
+ * Transformed classes can be either Module or standard class.
+ */
+ def transformClassBody(stats: List[c.Tree]): Tree = {
+ val contextVar = TermName(c.freshName("namingContext"))
+ val transformedBody = (new ClassBodyTransformer(contextVar)).transformTrees(stats)
+ // Note: passing "this" to popReturnContext is mandatory for propagation through non-module classes
+ q"""
+ val $contextVar = $globalNamingStack.pushContext()
+ ..$transformedBody
+ if($globalNamingStack.length == 1){
+ $contextVar.namePrefix("")
+ }
+ $globalNamingStack.popReturnContext(this, $contextVar)
+ """
+ }
+
+ /** Applies the val name transform to a method body, doing additional bookkeeping with the
+ * context to allow names to propagate and prefix through the function call stack.
+ */
+ def transformHierarchicalMethod(expr: c.Tree): Tree = {
+ val contextVar = TermName(c.freshName("namingContext"))
+ val transformedBody = (new MethodTransformer(contextVar)).transform(expr)
+
+ q"""{
+ val $contextVar = $globalNamingStack.pushContext()
+ $globalNamingStack.popReturnContext($transformedBody, $contextVar)
+ }
+ """
+ }
+
+ /** Applies naming transforms to vals in the annotated module or method.
+ *
+ * For methods, a hierarchical naming transform is used, where it will try to give objects names
+ * based on the call stack, assuming all functions on the stack are annotated as such and return
+ * a non-AnyVal object. Does not recurse into inner functions.
+ *
+ * For modules, this serves as the root of the call stack hierarchy for naming purposes. Methods
+ * will have chiselName annotations (non-recursively), but this does NOT affect inner classes.
+ *
+ * Basically rewrites all instances of:
+ * val name = expr
+ * to:
+ * val name = context.name(expr, name)
+ */
+ def chiselName(annottees: c.Tree*): c.Tree = {
+ var namedElts: Int = 0
+
+ val transformed = annottees.map(annottee => annottee match {
+ // scalastyle:off line.size.limit
+ case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" => {
+ val transformedStats = transformClassBody(stats)
+ namedElts += 1
+ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$transformedStats }"
+ }
+ case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" => {
+ annottee // Don't fail noisly when a companion object is passed in with the actual class def
+ }
+ // Currently disallow on traits, this won't work well with inheritance.
+ case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => {
+ val transformedExpr = transformHierarchicalMethod(expr)
+ namedElts += 1
+ q"$mods def $tname[..$tparams](...$paramss): $tpt = $transformedExpr"
+ }
+ case other => c.abort(c.enclosingPosition, s"@chiselName annotion may only be used on classes and methods, got ${showCode(other)}")
+ })
+
+ if (namedElts != 1) {
+ c.abort(c.enclosingPosition, s"@chiselName annotation did not match exactly one valid tree, got:\r\n${annottees.map(tree => showCode(tree)).mkString("\r\n\r\n")}")
+ }
+ // scalastyle:on line.size.limit
+
+ q"..$transformed"
+ }
+}
+
+@compileTimeOnly("enable macro paradise to expand macro annotations")
+class dump extends StaticAnnotation { // scalastyle:ignore class.name
+ def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.DebugTransforms.dump
+}
+@compileTimeOnly("enable macro paradise to expand macro annotations")
+class treedump extends StaticAnnotation { // scalastyle:ignore class.name
+ def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.DebugTransforms.treedump
+}
+@compileTimeOnly("enable macro paradise to expand macro annotations")
+class chiselName extends StaticAnnotation { // scalastyle:ignore class.name
+ def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.NamingTransforms.chiselName
+}