diff options
Diffstat (limited to 'plugin')
| -rw-r--r-- | plugin/README.md | 99 | ||||
| -rw-r--r-- | plugin/src/main/resources/scalac-plugin.xml | 4 | ||||
| -rw-r--r-- | plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala | 281 | ||||
| -rw-r--r-- | plugin/src/main/scala/chisel3/internal/plugin/ChiselComponent.scala | 242 | ||||
| -rw-r--r-- | plugin/src/main/scala/chisel3/internal/plugin/ChiselPlugin.scala | 81 |
5 files changed, 0 insertions, 707 deletions
diff --git a/plugin/README.md b/plugin/README.md deleted file mode 100644 index 762f4822..00000000 --- a/plugin/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Notes on the Compiler Plug-in - -The Chisel plugin provides some operations that are too difficult, or not possbile, -to implement through regular Scala code. - -# This documentation is for developers working on chisel internals. - -## Compiler plugin operations -These are the two things that the compile plugin does. - -1. Automatically generates the `cloneType` methods of Bundle -2. Changes the underlying mechanics of the `Bundle`s `elements` method in a way -that does not require the use of **reflection** -3. Future work: Make having a Seq[Data] in a bundle be a compiler error. See "Detecting Bundles with Seq[Data]" below. - -### 1. Generating `cloneType` method -As of Mar 18, 2021, PR #1826, generating the `cloneType` method (1. above) is now the default behavior. -The cloneType method used to be a tricky thing to write for chisel developers. -For historical purposes, here is the flag was used to control that prior to full adoption. -``` --P:chiselplugin:useBundlePlugin -``` - -### 2. Changing `Bundle#elements` method - -A `Bundle` has a default `elements` method that relies on **reflection**, which is slow and brittle, to access the list of -*fields* the bundle contains. -When enabled this second operation of the plugin examines -the `Bundle`s AST in order to determine the fields and then re-writes the underlying code of `elements`. -Technically, rewriting a lower level private method `_elementsImpl`. -It is expected that the using this feature will shortly become the default. - ->The plugin should not be enabled for the `main` chisel3 project because of internal considerations. -> It is enabled for the `Test` section. - -In the meantime, advanced users can try using the feature by adding the following flag to the scalac options in their -chisel projects. - -``` --P:chiselplugin:buildElementAccessor -``` - -For example in an `build.sbt` file adding the line -``` -scalacOptions += "-P:chiselplugin:genBundleElements", -``` -in the appropriate place. - -## Future work -### Detecting Bundles with Seq[Data] -Trying to have a `val Seq[Data]` (as opposed to a `val Vec[Data]` in a `Bundle` is a run time error. -Here is a block of code that could be added to the plugin to detect this case at compile time (with some refinement in -the detection mechanism): -```scala - if (member.isAccessor && typeIsSeqOfData(member.tpe) && !isIgnoreSeqInBundle(bundleSymbol)) { - global.reporter.error( - member.pos, - s"Bundle.field ${bundleSymbol.name}.${member.name} cannot be a Seq[Data]. " + - "Use Vec or MixedVec or mix in trait IgnoreSeqInBundle" - ) - } -``` -### Notes about working on the `_elementsImpl` generator for the plugin in `BundleComponent.scala` -In general the easiest way to develop and debug new code in the plugin is to use `println` statements. -Naively this can result in reams of text that can be very hard to look through. - -What I found to be useful was creating some wrappers for `println` that only printed when the `Bundles` had a particular name pattern. -- Create a regular expression string in the `BundleComponent` class -- Add a printf wrapper name `show` that checks the `Bundle`'s name against the regex -- For recursive code in `getAllBundleFields` create a different wrapper `indentShow` that indents debug lines -- Sprinkle calls to these wrappers as needed for debugging - -#### Bundle Regex -```scala - val bundleNameDebugRegex = "MyBundle.*" -``` -#### Add `show` wrapper -`show` should be inside `case bundle` block of the `transform` method in order to have access to the current `Bundle` - -```scala -def show(string: => String): Unit = { - if (bundle.symbol.name.toString.matches(bundleNameDebugRegex)) { - println(string) - } -} -``` -#### Add `indentShow` wrapper -This method can be added into `BundleComponent.scala` in the `transform` method after `case Bundle` -Inside of `getAllBundleFields` I added the following code that indented for each recursion up the current -`Bundle`'s hierarchy. -```scala -def indentShow(s: => String): Unit = { - val indentString = ("-" * depth) * 2 + "> " - s.split("\n").foreach { line => - show(indentString + line) - } -} -``` - diff --git a/plugin/src/main/resources/scalac-plugin.xml b/plugin/src/main/resources/scalac-plugin.xml deleted file mode 100644 index abe51d12..00000000 --- a/plugin/src/main/resources/scalac-plugin.xml +++ /dev/null @@ -1,4 +0,0 @@ -<plugin> - <name>chiselplugin</name> - <classname>chisel3.internal.plugin.ChiselPlugin</classname> -</plugin> diff --git a/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala b/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala deleted file mode 100644 index f9452f5a..00000000 --- a/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala +++ /dev/null @@ -1,281 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chisel3.internal.plugin - -import scala.collection.mutable -import scala.tools.nsc -import scala.tools.nsc.{Global, Phase} -import scala.tools.nsc.plugins.PluginComponent -import scala.tools.nsc.symtab.Flags -import scala.tools.nsc.transform.TypingTransformers - -/** Performs three operations - * 1) Records that this plugin ran on a bundle by adding a method - * `override protected def _usingPlugin: Boolean = true` - * 2) Constructs a cloneType method - * 3) Builds a `def elements` that is computed once in this plugin - * Eliminates needing reflection to discover the hardware fields of a `Bundle` - * - * @param global the environment - * @param arguments run time parameters to code - */ -private[plugin] class BundleComponent(val global: Global, arguments: ChiselPluginArguments) - extends PluginComponent - with TypingTransformers { - import global._ - - val phaseName: String = "chiselbundlephase" - val runsAfter: List[String] = "typer" :: Nil - def newPhase(prev: Phase): Phase = new BundlePhase(prev) - - private class BundlePhase(prev: Phase) extends StdPhase(prev) { - override def name: String = phaseName - def apply(unit: CompilationUnit): Unit = { - if (ChiselPlugin.runComponent(global, arguments)(unit)) { - unit.body = new MyTypingTransformer(unit).transform(unit.body) - } - } - } - - private class MyTypingTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { - - def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe - - val bundleTpe: Type = inferType(tq"chisel3.Bundle") - val recordTpe: Type = inferType(tq"chisel3.Record") - val autoCloneTpe: Type = inferType(tq"chisel3.experimental.AutoCloneType") - val dataTpe: Type = inferType(tq"chisel3.Data") - val ignoreSeqTpe: Type = inferType(tq"chisel3.IgnoreSeqInBundle") - val seqOfDataTpe: Type = inferType(tq"scala.collection.Seq[chisel3.Data]") - val someOfDataTpe: Type = inferType(tq"scala.Option[chisel3.Data]") - val itStringAnyTpe: Type = inferType(tq"scala.collection.Iterable[(String,Any)]") - - // Not cached because it should only be run once per class (thus once per Type) - def isABundle(sym: Symbol): Boolean = { sym.tpe <:< bundleTpe } - - def isARecord(sym: Symbol): Boolean = { sym.tpe <:< recordTpe } - - def isAnAutoCloneType(sym: Symbol): Boolean = { sym.tpe <:< autoCloneTpe } - - def isIgnoreSeqInBundle(sym: Symbol): Boolean = { sym.tpe <:< ignoreSeqTpe } - - def isSeqOfData(sym: Symbol): Boolean = { - val tpe = sym.tpe - tpe match { - case NullaryMethodType(resultType) => - resultType <:< seqOfDataTpe - case _ => - false - } - } - - def isOptionOfData(symbol: Symbol): Boolean = { - val tpe = symbol.tpe - tpe match { - case NullaryMethodType(resultType) => - resultType <:< someOfDataTpe - case _ => - false - } - } - def isExactBundle(sym: Symbol): Boolean = { sym.tpe =:= bundleTpe } - - // Cached because this is run on every argument to every Bundle - val isDataCache = new mutable.HashMap[Type, Boolean] - def isData(sym: Symbol): Boolean = isDataCache.getOrElseUpdate(sym.tpe, sym.tpe <:< dataTpe) - - def cloneTypeFull(tree: Tree): Tree = - localTyper.typed(q"chisel3.experimental.DataMirror.internal.chiselTypeClone[${tree.tpe}]($tree)") - - def isNullaryMethodNamed(name: String, defdef: DefDef): Boolean = - defdef.name.decodedName.toString == name && defdef.tparams.isEmpty && defdef.vparamss.isEmpty - - def isVarArgs(sym: Symbol): Boolean = definitions.isRepeatedParamType(sym.tpe) - - def getConstructorAndParams(body: List[Tree], isBundle: Boolean): (Option[DefDef], Seq[Symbol]) = { - val paramAccessors = mutable.ListBuffer[Symbol]() - var primaryConstructor: Option[DefDef] = None - body.foreach { - case acc: ValDef if acc.symbol.isParamAccessor => - paramAccessors += acc.symbol - case con: DefDef if con.symbol.isPrimaryConstructor => - primaryConstructor = Some(con) - case d: DefDef if isNullaryMethodNamed("_cloneTypeImpl", d) => - val msg = "Users cannot override _cloneTypeImpl. Let the compiler plugin generate it." - global.reporter.error(d.pos, msg) - case d: DefDef if isNullaryMethodNamed("_elementsImpl", d) && isBundle => - val msg = "Users cannot override _elementsImpl. Let the compiler plugin generate it." - global.reporter.error(d.pos, msg) - case d: DefDef if isNullaryMethodNamed("_usingPlugin", d) && isBundle => - val msg = "Users cannot override _usingPlugin, it is for the compiler plugin's use only." - global.reporter.error(d.pos, msg) - case d: DefDef if isNullaryMethodNamed("cloneType", d) => - val prefix = if (isBundle) "Bundles" else "Records extending AutoCloneType" - val msg = s"$prefix cannot override cloneType. Let the compiler plugin generate it." - global.reporter.error(d.pos, msg) - case _ => - } - (primaryConstructor, paramAccessors.toList) - } - - def warnOnCloneType(body: List[Tree]): Unit = { - body.foreach { - case d: DefDef if isNullaryMethodNamed("cloneType", d) => - val msg = "It is no longer necessary to implement cloneType. " + - "Mix in chisel3.experimental.AutoCloneType to let the compiler plugin generate it. " + - "This will become an error in Chisel 3.6." - global.reporter.warning(d.pos, msg) - case _ => // Do nothing - } - } - - def generateAutoCloneType(record: ClassDef, thiz: global.This, isBundle: Boolean): Option[Tree] = { - val (con, params) = getConstructorAndParams(record.impl.body, isBundle) - if (con.isEmpty) { - global.reporter.warning(record.pos, "Unable to determine primary constructor!") - return None - } - - val constructor = con.get - - // The params have spaces after them (Scalac implementation detail) - val paramLookup: String => Symbol = params.map(sym => sym.name.toString.trim -> sym).toMap - - // Create a this.<ref> for each field matching order of constructor arguments - // List of Lists because we can have multiple parameter lists - val conArgs: List[List[Tree]] = - constructor.vparamss.map(_.map { vp => - val p = paramLookup(vp.name.toString) - // Make this.<ref> - val select = gen.mkAttributedSelect(thiz.asInstanceOf[Tree], p) - // Clone any Data parameters to avoid field aliasing, need full clone to include direction - val cloned = if (isData(vp.symbol)) cloneTypeFull(select.asInstanceOf[Tree]) else select - // Need to splat varargs - if (isVarArgs(vp.symbol)) q"$cloned: _*" else cloned - }) - - val tparamList = record.tparams.map { t => Ident(t.symbol) } - val ttpe = - if (tparamList.nonEmpty) AppliedTypeTree(Ident(record.symbol), tparamList) else Ident(record.symbol) - val newUntyped = New(ttpe, conArgs) - val neww = localTyper.typed(newUntyped) - - // Create the symbol for the method and have it be associated with the Record class - val cloneTypeSym = - record.symbol.newMethod(TermName("_cloneTypeImpl"), record.symbol.pos.focus, Flag.OVERRIDE | Flag.PROTECTED) - // Handwritten cloneTypes don't have the Method flag set, unclear if it matters - cloneTypeSym.resetFlag(Flags.METHOD) - - // Need to set the return type correctly for the override to work - // For binary compatibility reasons in 3.5, Bundles have to return chisel3.Bundle - val returnType = if (isBundle) bundleTpe else recordTpe - cloneTypeSym.setInfo(NullaryMethodType(returnType)) - - Some(localTyper.typed(DefDef(cloneTypeSym, neww))) - } - - def generateElements(bundle: ClassDef, thiz: global.This): Tree = { - /* extract the true fields from the super classes a given bundle - * depth argument can be helpful for debugging - */ - def getAllBundleFields(bundleSymbol: Symbol, depth: Int = 0): List[(String, Tree)] = { - - def isBundleField(member: Symbol): Boolean = { - if (!member.isAccessor) { - false - } else if (isData(member.tpe.typeSymbol)) { - true - } else if (isOptionOfData(member)) { - true - } else if (isSeqOfData(member)) { - // This field is passed along, even though it is illegal - // An error for this will be generated in `Bundle.elements` - // It would be possible here to check for Seq[Data] and make a compiler error, but - // that would be a API error difference. See reference in docs/chisel-plugin.md - // If Bundle is subclass of IgnoreSeqInBundle then don't pass this field along - - !isIgnoreSeqInBundle(bundleSymbol) - } else { - // none of the above - false - } - } - - val currentFields = bundleSymbol.info.members.flatMap { - - case member if member.isPublic => - if (isBundleField(member)) { - // The params have spaces after them (Scalac implementation detail) - Some(member.name.toString.trim -> gen.mkAttributedSelect(thiz.asInstanceOf[Tree], member)) - } else { - None - } - - case _ => None - }.toList - - val allParentFields = bundleSymbol.parentSymbols.flatMap { parentSymbol => - val fieldsFromParent = if (depth < 1 && !isExactBundle(bundleSymbol)) { - val foundFields = getAllBundleFields(parentSymbol, depth + 1) - foundFields - } else { - List() - } - fieldsFromParent - } - allParentFields ++ currentFields - } - - val elementArgs = getAllBundleFields(bundle.symbol) - - val elementsImplSym = - bundle.symbol.newMethod(TermName("_elementsImpl"), bundle.symbol.pos.focus, Flag.OVERRIDE | Flag.PROTECTED) - elementsImplSym.resetFlag(Flags.METHOD) - elementsImplSym.setInfo(NullaryMethodType(itStringAnyTpe)) - - val elementsImpl = localTyper.typed( - DefDef(elementsImplSym, q"scala.collection.immutable.Vector.apply[(String, Any)](..$elementArgs)") - ) - - elementsImpl - } - - override def transform(tree: Tree): Tree = tree match { - - case record: ClassDef if isARecord(record.symbol) && !record.mods.hasFlag(Flag.ABSTRACT) => - val isBundle: Boolean = isABundle(record.symbol) - val isAutoCloneType: Boolean = isAnAutoCloneType(record.symbol) - - if (!isAutoCloneType) { - warnOnCloneType(record.impl.body) - // Other than warning, there is nothing to do on Records that don't mixin AutoCloneType - return super.transform(record) - } - - val thiz: global.This = gen.mkAttributedThis(record.symbol) - - // ==================== Generate _cloneTypeImpl ==================== - val cloneTypeImplOpt = generateAutoCloneType(record, thiz, isBundle) - - // ==================== Generate val elements (Bundles only) ==================== - val elementsImplOpt = - if (isBundle && arguments.genBundleElements) Some(generateElements(record, thiz)) else None - - // ==================== Generate _usingPlugin ==================== - val usingPluginOpt = if (isBundle) { - // Unclear why quasiquotes work here but didn't for cloneTypeSym, maybe they could. - Some(localTyper.typed(q"override protected def _usingPlugin: Boolean = true")) - } else { - None - } - - val withMethods = deriveClassDef(record) { t => - deriveTemplate(t)(_ ++ cloneTypeImplOpt ++ usingPluginOpt ++ elementsImplOpt) - } - - super.transform(localTyper.typed(withMethods)) - - case _ => super.transform(tree) - } - } -} diff --git a/plugin/src/main/scala/chisel3/internal/plugin/ChiselComponent.scala b/plugin/src/main/scala/chisel3/internal/plugin/ChiselComponent.scala deleted file mode 100644 index c0efb4ed..00000000 --- a/plugin/src/main/scala/chisel3/internal/plugin/ChiselComponent.scala +++ /dev/null @@ -1,242 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chisel3.internal.plugin - -import scala.collection.mutable -import scala.reflect.internal.Flags -import scala.tools.nsc -import scala.tools.nsc.{Global, Phase} -import scala.tools.nsc.plugins.PluginComponent -import scala.tools.nsc.transform.TypingTransformers - -// The component of the chisel plugin. Not sure exactly what the difference is between -// a Plugin and a PluginComponent. -class ChiselComponent(val global: Global, arguments: ChiselPluginArguments) - extends PluginComponent - with TypingTransformers { - import global._ - val runsAfter: List[String] = List[String]("typer") - val phaseName: String = "chiselcomponent" - def newPhase(_prev: Phase): ChiselComponentPhase = new ChiselComponentPhase(_prev) - class ChiselComponentPhase(prev: Phase) extends StdPhase(prev) { - override def name: String = phaseName - def apply(unit: CompilationUnit): Unit = { - if (ChiselPlugin.runComponent(global, arguments)(unit)) { - unit.body = new MyTypingTransformer(unit).transform(unit.body) - } - } - } - - class MyTypingTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { - - private def shouldMatchGen(bases: Tree*): Type => Boolean = { - val cache = mutable.HashMap.empty[Type, Boolean] - val baseTypes = bases.map(inferType) - - // If subtype of one of the base types, it's a match! - def terminate(t: Type): Boolean = baseTypes.exists(t <:< _) - - // Recurse through subtype hierarchy finding containers - // Seen is only updated when we recurse into type parameters, thus it is typically small - def recShouldMatch(s: Type, seen: Set[Type]): Boolean = { - def outerMatches(t: Type): Boolean = { - val str = t.toString - str.startsWith("Option[") || str.startsWith("Iterable[") - } - if (terminate(s)) { - true - } else if (seen.contains(s)) { - false - } else if (outerMatches(s)) { - // These are type parameters, loops *are* possible here - recShouldMatch(s.typeArgs.head, seen + s) - } else { - // This is the standard inheritance hierarchy, Scalac catches loops here - s.parents.exists(p => recShouldMatch(p, seen)) - } - } - - // If doesn't match container pattern, exit early - def earlyExit(t: Type): Boolean = { - !(t.matchesPattern(inferType(tq"Iterable[_]")) || t.matchesPattern(inferType(tq"Option[_]"))) - } - - // Return function so that it captures the cache - { q: Type => - cache.getOrElseUpdate( - q, { - // First check if a match, then check early exit, then recurse - if (terminate(q)) { - true - } else if (earlyExit(q)) { - false - } else { - recShouldMatch(q, Set.empty) - } - } - ) - } - } - - private val shouldMatchData: Type => Boolean = shouldMatchGen(tq"chisel3.Data") - // Checking for all chisel3.internal.NamedComponents, but since it is internal, we instead have - // to match the public subtypes - private val shouldMatchNamedComp: Type => Boolean = - shouldMatchGen( - tq"chisel3.Data", - tq"chisel3.MemBase[_]", - tq"chisel3.VerificationStatement" - ) - private val shouldMatchModule: Type => Boolean = shouldMatchGen(tq"chisel3.experimental.BaseModule") - private val shouldMatchChiselPrefixed: Type => Boolean = - shouldMatchGen( - tq"chisel3.experimental.AffectsChiselPrefix" - ) - - // Given a type tree, infer the type and return it - private def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe - - // Indicates whether a ValDef is properly formed to get name - private def okVal(dd: ValDef): Boolean = { - - // These were found through trial and error - def okFlags(mods: Modifiers): Boolean = { - val badFlags = Set( - Flag.PARAM, - Flag.SYNTHETIC, - Flag.DEFERRED, - Flags.TRIEDCOOKING, - Flags.CASEACCESSOR, - Flags.PARAMACCESSOR - ) - badFlags.forall { x => !mods.hasFlag(x) } - } - - // Ensure expression isn't null, as you can't call `null.autoName("myname")` - val isNull = dd.rhs match { - case Literal(Constant(null)) => true - case _ => false - } - - okFlags(dd.mods) && !isNull && dd.rhs != EmptyTree - } - // TODO Unify with okVal - private def okUnapply(dd: ValDef): Boolean = { - - // These were found through trial and error - def okFlags(mods: Modifiers): Boolean = { - val badFlags = Set( - Flag.PARAM, - Flag.DEFERRED, - Flags.TRIEDCOOKING, - Flags.CASEACCESSOR, - Flags.PARAMACCESSOR - ) - val goodFlags = Set( - Flag.SYNTHETIC, - Flag.ARTIFACT - ) - goodFlags.forall(f => mods.hasFlag(f)) && badFlags.forall(f => !mods.hasFlag(f)) - } - - // Ensure expression isn't null, as you can't call `null.autoName("myname")` - val isNull = dd.rhs match { - case Literal(Constant(null)) => true - case _ => false - } - val tpe = inferType(dd.tpt) - definitions.isTupleType(tpe) && okFlags(dd.mods) && !isNull && dd.rhs != EmptyTree - } - - private def findUnapplyNames(tree: Tree): Option[List[String]] = { - val applyArgs: Option[List[Tree]] = tree match { - case Match(_, List(CaseDef(_, _, Apply(_, args)))) => Some(args) - case _ => None - } - applyArgs.flatMap { args => - var ok = true - val result = mutable.ListBuffer[String]() - args.foreach { - case Ident(TermName(name)) => result += name - // Anything unexpected and we abort - case _ => ok = false - } - if (ok) Some(result.toList) else None - } - } - - // Whether this val is directly enclosed by a Bundle type - private def inBundle(dd: ValDef): Boolean = { - dd.symbol.logicallyEnclosingMember.thisType <:< inferType(tq"chisel3.Bundle") - } - - private def stringFromTermName(name: TermName): String = - name.toString.trim() // Remove trailing space (Scalac implementation detail) - - // Method called by the compiler to modify source tree - override def transform(tree: Tree): Tree = tree match { - // Check if a subtree is a candidate - case dd @ ValDef(mods, name, tpt, rhs) if okVal(dd) => - val tpe = inferType(tpt) - val isData = shouldMatchData(tpe) - val isNamedComp = isData || shouldMatchNamedComp(tpe) - val isPrefixed = isNamedComp || shouldMatchChiselPrefixed(tpe) - - // If a Data and in a Bundle, just get the name but not a prefix - if (isData && inBundle(dd)) { - val str = stringFromTermName(name) - val newRHS = transform(rhs) // chisel3.internal.plugin.autoNameRecursively - val named = q"chisel3.internal.plugin.autoNameRecursively($str)($newRHS)" - treeCopy.ValDef(dd, mods, name, tpt, localTyper.typed(named)) - } - // If a Data or a Memory, get the name and a prefix - else if (isData || isPrefixed) { - val str = stringFromTermName(name) - // Starting with '_' signifies a temporary, we ignore it for prefixing because we don't - // want double "__" in names when the user is just specifying a temporary - val prefix = if (str.head == '_') str.tail else str - val newRHS = transform(rhs) - val prefixed = q"chisel3.experimental.prefix.apply[$tpt](name=$prefix)(f=$newRHS)" - - val named = - if (isNamedComp) { - // Only name named components (not things that are merely prefixed) - q"chisel3.internal.plugin.autoNameRecursively($str)($prefixed)" - } else { - prefixed - } - - treeCopy.ValDef(dd, mods, name, tpt, localTyper.typed(named)) - } - // If an instance, just get a name but no prefix - else if (shouldMatchModule(tpe)) { - val str = stringFromTermName(name) - val newRHS = transform(rhs) - val named = q"chisel3.internal.plugin.autoNameRecursively($str)($newRHS)" - treeCopy.ValDef(dd, mods, name, tpt, localTyper.typed(named)) - } else { - // Otherwise, continue - super.transform(tree) - } - case dd @ ValDef(mods, name, tpt, rhs @ Match(_, _)) if okUnapply(dd) => - val tpe = inferType(tpt) - val fieldsOfInterest: List[Boolean] = tpe.typeArgs.map(shouldMatchData) - // Only transform if at least one field is of interest - if (fieldsOfInterest.reduce(_ || _)) { - findUnapplyNames(rhs) match { - case Some(names) => - val onames: List[Option[String]] = - fieldsOfInterest.zip(names).map { case (ok, name) => if (ok) Some(name) else None } - val named = q"chisel3.internal.plugin.autoNameRecursivelyProduct($onames)($rhs)" - treeCopy.ValDef(dd, mods, name, tpt, localTyper.typed(named)) - case None => // It's not clear how this could happen but we don't want to crash - super.transform(tree) - } - } else { - super.transform(tree) - } - // Otherwise, continue - case _ => super.transform(tree) - } - } -} diff --git a/plugin/src/main/scala/chisel3/internal/plugin/ChiselPlugin.scala b/plugin/src/main/scala/chisel3/internal/plugin/ChiselPlugin.scala deleted file mode 100644 index 9bf8c657..00000000 --- a/plugin/src/main/scala/chisel3/internal/plugin/ChiselPlugin.scala +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chisel3.internal.plugin - -import scala.tools.nsc -import nsc.Global -import nsc.plugins.{Plugin, PluginComponent} -import scala.reflect.internal.util.NoPosition -import scala.collection.mutable - -private[plugin] case class ChiselPluginArguments( - val skipFiles: mutable.HashSet[String] = mutable.HashSet.empty, - var genBundleElements: Boolean = false) { - def useBundlePluginOpt = "useBundlePlugin" - def useBundlePluginFullOpt = s"-P:${ChiselPlugin.name}:$useBundlePluginOpt" - def genBundleElementsOpt = "genBundleElements" - def genBundleElementsFullOpt = s"-P:${ChiselPlugin.name}:$genBundleElementsOpt" - // Annoying because this shouldn't be used by users - def skipFilePluginOpt = "INTERNALskipFile:" - def skipFilePluginFullOpt = s"-P:${ChiselPlugin.name}:$skipFilePluginOpt" -} - -object ChiselPlugin { - val name = "chiselplugin" - - // Also logs why the component was not run - private[plugin] def runComponent( - global: Global, - arguments: ChiselPluginArguments - )(unit: global.CompilationUnit - ): Boolean = { - // This plugin doesn't work on Scala 2.11 nor Scala 3. Rather than complicate the sbt build flow, - // instead we just check the version and if its an early Scala version, the plugin does nothing - val scalaVersion = scala.util.Properties.versionNumberString.split('.') - val scalaVersionOk = scalaVersion(0).toInt == 2 && scalaVersion(1).toInt >= 12 - val skipFile = arguments.skipFiles(unit.source.file.path) - if (scalaVersionOk && !skipFile) { - true - } else { - val reason = if (!scalaVersionOk) { - s"invalid Scala version '${scala.util.Properties.versionNumberString}'" - } else { - s"file skipped via '${arguments.skipFilePluginFullOpt}'" - } - // Enable this with scalacOption '-Ylog:chiselbundlephase' - global.log(s"Skipping BundleComponent on account of $reason.") - false - } - } -} - -// The plugin to be run by the Scala compiler during compilation of Chisel code -class ChiselPlugin(val global: Global) extends Plugin { - val name = ChiselPlugin.name - val description = "Plugin for Chisel 3 Hardware Description Language" - private val arguments = ChiselPluginArguments() - val components: List[PluginComponent] = List[PluginComponent]( - new ChiselComponent(global, arguments), - new BundleComponent(global, arguments) - ) - - override def init(options: List[String], error: String => Unit): Boolean = { - for (option <- options) { - if (option == arguments.useBundlePluginOpt) { - val msg = s"'${arguments.useBundlePluginFullOpt}' is now default behavior, you can stop using the scalacOption." - global.reporter.warning(NoPosition, msg) - } else if (option.startsWith(arguments.skipFilePluginOpt)) { - val filename = option.stripPrefix(arguments.skipFilePluginOpt) - arguments.skipFiles += filename - // Be annoying and warn because users are not supposed to use this - val msg = s"Option -P:${ChiselPlugin.name}:$option should only be used for internal chisel3 compiler purposes!" - global.reporter.warning(NoPosition, msg) - } else if (option == arguments.genBundleElementsOpt) { - arguments.genBundleElements = true - } else { - error(s"Option not understood: '$option'") - } - } - true - } -} |
