From 1d81119e4b50d1b130ea5df6f4ba076b7f27c9ac Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Mon, 23 Mar 2020 09:29:02 -0700 Subject: Add NoChiselNamePrefix to ignore instances in @chiselName (#1383) Add trait chisel3.experimental.NoChiselNamePrefix which causes @chiselName to skip naming of the instance effectively preventing it from prefixing any vals inside the instance. It can be applied to classes such that all instances of that class have this property, or to individual instances (via creating an anonymous class inline). Also add basic ScalaDoc for NoChiselNamePrefix and chiselName.--- .../internal/naming/NamingAnnotations.scala | 205 +++++++++++++++++++++ .../internal/sourceinfo/NamingAnnotations.scala | 205 --------------------- 2 files changed, 205 insertions(+), 205 deletions(-) create mode 100644 coreMacros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala delete mode 100644 coreMacros/src/main/scala/chisel3/internal/sourceinfo/NamingAnnotations.scala (limited to 'coreMacros/src/main') diff --git a/coreMacros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala b/coreMacros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala new file mode 100644 index 00000000..bf4879ec --- /dev/null +++ b/coreMacros/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("") + 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 +} diff --git a/coreMacros/src/main/scala/chisel3/internal/sourceinfo/NamingAnnotations.scala b/coreMacros/src/main/scala/chisel3/internal/sourceinfo/NamingAnnotations.scala deleted file mode 100644 index bf4879ec..00000000 --- a/coreMacros/src/main/scala/chisel3/internal/sourceinfo/NamingAnnotations.scala +++ /dev/null @@ -1,205 +0,0 @@ -// 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("") - 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 -} -- cgit v1.2.3