From c5861176887bfa529277e686df09a42aeceb6cd7 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 29 Apr 2021 16:18:06 -0700 Subject: Scala 2.13 support (#1751) --- .github/workflows/test.yml | 2 +- build.sbt | 31 ++- .../scala/collection/immutable/package.scala | 13 ++ core/src/main/scala/chisel3/Aggregate.scala | 12 +- core/src/main/scala/chisel3/Bits.scala | 1 + core/src/main/scala/chisel3/Data.scala | 45 +++-- core/src/main/scala/chisel3/Module.scala | 4 +- core/src/main/scala/chisel3/Num.scala | 2 +- core/src/main/scala/chisel3/RawModule.scala | 1 - core/src/main/scala/chisel3/internal/Binding.scala | 4 +- core/src/main/scala/chisel3/internal/Builder.scala | 2 +- core/src/main/scala/chisel3/internal/Error.scala | 6 +- core/src/main/scala/chisel3/internal/Namer.scala | 14 +- core/src/main/scala/chisel3/package.scala | 3 - .../chisel3/internal/plugin/BundleComponent.scala | 136 ------------- .../chisel3/internal/plugin/ChiselComponent.scala | 215 --------------------- .../chisel3/internal/plugin/ChiselPlugin.scala | 39 ---- .../chisel3/internal/plugin/BundleComponent.scala | 136 +++++++++++++ .../chisel3/internal/plugin/ChiselComponent.scala | 215 +++++++++++++++++++++ .../chisel3/internal/plugin/ChiselPlugin.scala | 39 ++++ src/main/scala/chisel3/aop/Select.scala | 8 +- .../chisel3/aop/injecting/InjectingAspect.scala | 2 +- src/main/scala/chisel3/compatibility.scala | 2 +- src/main/scala/chisel3/util/MixedVec.scala | 4 + src/test/scala/chiselTests/ChiselSpec.scala | 2 +- src/test/scala/chiselTests/ExtModule.scala | 16 +- src/test/scala/examples/VendingMachineUtils.scala | 2 +- 27 files changed, 495 insertions(+), 461 deletions(-) create mode 100644 core/src/main/scala-2.12/scala/collection/immutable/package.scala delete mode 100644 plugin/src/main/scala-2.12/chisel3/internal/plugin/BundleComponent.scala delete mode 100644 plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselComponent.scala delete mode 100644 plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala create mode 100644 plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala create mode 100644 plugin/src/main/scala/chisel3/internal/plugin/ChiselComponent.scala create mode 100644 plugin/src/main/scala/chisel3/internal/plugin/ChiselPlugin.scala diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6d9fe475..5d321bfa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - scala: [2.12.13] + scala: [2.13.5, 2.12.13] container: image: ucbbar/chisel3-tools options: --user github --entrypoint /bin/bash diff --git a/build.sbt b/build.sbt index a9e18216..60b83a3e 100644 --- a/build.sbt +++ b/build.sbt @@ -15,12 +15,23 @@ lazy val commonSettings = Seq ( organization := "edu.berkeley.cs", version := "3.5-SNAPSHOT", autoAPIMappings := true, - scalaVersion := "2.12.13", - crossScalaVersions := Seq("2.12.13"), - scalacOptions := Seq("-deprecation", "-feature", - ), + scalaVersion := "2.13.5", + crossScalaVersions := Seq("2.13.5", "2.12.13"), + scalacOptions := Seq("-deprecation", "-feature"), libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value, - addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full), + // Macros paradise is integrated into 2.13 but requires a scalacOption + scalacOptions ++= { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, n)) if n >= 13 => "-Ymacro-annotations" :: Nil + case _ => Nil + } + }, + libraryDependencies ++= { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, n)) if n >= 13 => Nil + case _ => compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full) :: Nil + } + } ) lazy val publishSettings = Seq ( @@ -93,6 +104,12 @@ lazy val pluginScalaVersions = Seq( "2.12.11", "2.12.12", "2.12.13", + "2.13.0", + "2.13.1", + "2.13.2", + "2.13.3", + "2.13.4", + "2.13.5" ) lazy val plugin = (project in file("plugin")). @@ -108,9 +125,7 @@ lazy val plugin = (project in file("plugin")). crossTarget := { // workaround for https://github.com/sbt/sbt/issues/5097 target.value / s"scala-${scalaVersion.value}" - }, - // Only publish for Scala 2.12 - publish / skip := !scalaVersion.value.startsWith("2.12") + } ). settings( mimaPreviousArtifacts := { diff --git a/core/src/main/scala-2.12/scala/collection/immutable/package.scala b/core/src/main/scala-2.12/scala/collection/immutable/package.scala new file mode 100644 index 00000000..7ae87d9b --- /dev/null +++ b/core/src/main/scala-2.12/scala/collection/immutable/package.scala @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: Apache-2.0 + +package scala.collection + +import scala.collection.immutable.ListMap + +package object immutable { + val SeqMap = ListMap + type SeqMap[K, +V] = ListMap[K, V] + + val VectorMap = ListMap + type VectorMap[K, +V] = ListMap[K, V] +} diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index c0b965b6..0031e53b 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -4,7 +4,7 @@ package chisel3 import chisel3.experimental.VecLiterals.AddVecLiteralConstructor -import scala.collection.immutable.ListMap +import scala.collection.immutable.{SeqMap, VectorMap} import scala.collection.mutable.{HashSet, LinkedHashMap} import scala.language.experimental.macros import chisel3.experimental.{BaseModule, BundleLiteralException, ChiselEnum, EnumType, VecLiteralException} @@ -516,7 +516,7 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) } } - clone.bind(VecLitBinding(ListMap(vecLitLinkedMap.toSeq:_*))) + clone.bind(VecLitBinding(VectorMap(vecLitLinkedMap.toSeq:_*))) clone } } @@ -595,7 +595,7 @@ object VecInit extends SourceInfoDoc { /** A trait for [[Vec]]s containing common hardware generators for collection * operations. */ -trait VecLike[T <: Data] extends collection.IndexedSeq[T] with HasId with SourceInfoDoc { +trait VecLike[T <: Data] extends IndexedSeq[T] with HasId with SourceInfoDoc { def apply(p: UInt): T = macro CompileOptionsTransform.pArg /** @group SourceInfoTransformMacro */ @@ -835,7 +835,7 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio s"$className$bindingString" } - val elements: ListMap[String, Data] + def elements: SeqMap[String, Data] /** Name for Pretty Printing */ def className: String = this.getClass.getSimpleName @@ -958,7 +958,7 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record { * assert(uint === "h12345678".U) // This will pass * }}} */ - final lazy val elements: ListMap[String, Data] = { + final lazy val elements: SeqMap[String, Data] = { val nameMap = LinkedHashMap[String, Data]() for (m <- getPublicFields(classOf[Bundle])) { getBundleField(m) match { @@ -985,7 +985,7 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record { } } } - ListMap(nameMap.toSeq sortWith { case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn)) }: _*) + VectorMap(nameMap.toSeq sortWith { case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn)) }: _*) } /** diff --git a/core/src/main/scala/chisel3/Bits.scala b/core/src/main/scala/chisel3/Bits.scala index 17829143..aa5b1abb 100644 --- a/core/src/main/scala/chisel3/Bits.scala +++ b/core/src/main/scala/chisel3/Bits.scala @@ -1147,6 +1147,7 @@ sealed class Bool() extends UInt(1.W) with Reset { package experimental { import chisel3.internal.firrtl.BinaryPoint + import chisel3.internal.requireIsHardware // Fix ambiguous import /** Chisel types that have binary points support retrieving * literal values as `Double` or `BigDecimal` diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 0241f248..0832161e 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -122,6 +122,7 @@ object ActualDirection { } package experimental { + import chisel3.internal.requireIsHardware // Fix ambiguous import /** Experimental hardware construction reflection API */ @@ -763,35 +764,33 @@ object WireDefault { } } -package internal { - /** RHS (source) for Invalidate API. - * Causes connection logic to emit a DefInvalid when connected to an output port (or wire). - */ - private[chisel3] object InternalDontCare extends Element { - // This object should be initialized before we execute any user code that refers to it, - // otherwise this "Chisel" object will end up on the UserModule's id list. - // We make it private to chisel3 so it has to be accessed through the package object. +/** RHS (source) for Invalidate API. + * Causes connection logic to emit a DefInvalid when connected to an output port (or wire). + */ +final case object DontCare extends Element { + // This object should be initialized before we execute any user code that refers to it, + // otherwise this "Chisel" object will end up on the UserModule's id list. + // We make it private to chisel3 so it has to be accessed through the package object. - private[chisel3] override val width: Width = UnknownWidth() + private[chisel3] override val width: Width = UnknownWidth() - bind(DontCareBinding(), SpecifiedDirection.Output) - override def cloneType: this.type = DontCare + bind(DontCareBinding(), SpecifiedDirection.Output) + override def cloneType: this.type = DontCare - override def toString: String = "DontCare()" + override def toString: String = "DontCare()" - override def litOption: Option[BigInt] = None + override def litOption: Option[BigInt] = None - def toPrintable: Printable = PString("DONTCARE") + def toPrintable: Printable = PString("DONTCARE") - private[chisel3] def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { - Builder.error("connectFromBits: DontCare cannot be a connection sink (LHS)") - } + private[chisel3] def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { + Builder.error("connectFromBits: DontCare cannot be a connection sink (LHS)") + } - def do_asUInt(implicit sourceInfo: chisel3.internal.sourceinfo.SourceInfo, compileOptions: CompileOptions): UInt = { - Builder.error("DontCare does not have a UInt representation") - 0.U - } - // DontCare's only match themselves. - private[chisel3] def typeEquivalent(that: Data): Boolean = that == DontCare + def do_asUInt(implicit sourceInfo: chisel3.internal.sourceinfo.SourceInfo, compileOptions: CompileOptions): UInt = { + Builder.error("DontCare does not have a UInt representation") + 0.U } + // DontCare's only match themselves. + private[chisel3] def typeEquivalent(that: Data): Boolean = that == DontCare } diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala index ede9ccc6..b204be8d 100644 --- a/core/src/main/scala/chisel3/Module.scala +++ b/core/src/main/scala/chisel3/Module.scala @@ -4,9 +4,7 @@ package chisel3 import scala.collection.immutable.ListMap import scala.collection.mutable.{ArrayBuffer, HashMap} -import scala.collection.JavaConversions._ import scala.language.experimental.macros -import java.util.IdentityHashMap import chisel3.internal._ import chisel3.internal.Builder._ @@ -139,6 +137,8 @@ abstract class Module(implicit moduleCompileOptions: CompileOptions) extends Raw package experimental { + import chisel3.internal.requireIsChiselType // Fix ambiguous import + object IO { /** Constructs a port for the current Module * diff --git a/core/src/main/scala/chisel3/Num.scala b/core/src/main/scala/chisel3/Num.scala index e1af9bdb..70f2fbf0 100644 --- a/core/src/main/scala/chisel3/Num.scala +++ b/core/src/main/scala/chisel3/Num.scala @@ -226,7 +226,7 @@ trait NumObject { */ def toBigInt(x: BigDecimal, binaryPoint: Int): BigInt = { val multiplier = math.pow(2, binaryPoint) - val result = (x * multiplier).rounded.toBigInt() + val result = (x * multiplier).rounded.toBigInt result } diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala index d2ba6e84..f678c587 100644 --- a/core/src/main/scala/chisel3/RawModule.scala +++ b/core/src/main/scala/chisel3/RawModule.scala @@ -4,7 +4,6 @@ package chisel3 import scala.collection.mutable.{ArrayBuffer, HashMap} import scala.util.Try -import scala.collection.JavaConversions._ import scala.language.experimental.macros import chisel3.experimental.BaseModule diff --git a/core/src/main/scala/chisel3/internal/Binding.scala b/core/src/main/scala/chisel3/internal/Binding.scala index 8a3c4330..300803ce 100644 --- a/core/src/main/scala/chisel3/internal/Binding.scala +++ b/core/src/main/scala/chisel3/internal/Binding.scala @@ -6,7 +6,7 @@ import chisel3._ import chisel3.experimental.BaseModule import chisel3.internal.firrtl.LitArg -import scala.collection.immutable.ListMap +import scala.collection.immutable.VectorMap /** Requires that a node is hardware ("bound") */ @@ -126,4 +126,4 @@ case class ElementLitBinding(litArg: LitArg) extends LitBinding // Literal binding attached to the root of a Bundle, containing literal values of its children. case class BundleLitBinding(litMap: Map[Data, LitArg]) extends LitBinding // Literal binding attached to the root of a Vec, containing literal values of its children. -case class VecLitBinding(litMap: ListMap[Data, LitArg]) extends LitBinding +case class VecLitBinding(litMap: VectorMap[Data, LitArg]) extends LitBinding diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index 084bdc88..e1e4d460 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -655,7 +655,7 @@ private[chisel3] object Builder extends LazyLogging { errors.checkpoint() logger.warn("Done elaborating.") - (Circuit(components.last.name, components, annotations), mod) + (Circuit(components.last.name, components.toSeq, annotations.toSeq), mod) } } initializeSingletons() diff --git a/core/src/main/scala/chisel3/internal/Error.scala b/core/src/main/scala/chisel3/internal/Error.scala index 134f4c87..454be360 100644 --- a/core/src/main/scala/chisel3/internal/Error.scala +++ b/core/src/main/scala/chisel3/internal/Error.scala @@ -45,7 +45,7 @@ object ExceptionHelpers { } // Step 1: Remove elements from the top in the package trimlist - ((a: Array[StackTraceElement]) => a.view.dropWhile(inTrimlist)) + ((a: Array[StackTraceElement]) => a.dropWhile(inTrimlist)) // Step 2: Optionally remove elements from the bottom until the anchor .andThen(_.reverse) .andThen( a => @@ -125,10 +125,10 @@ class ChiselException(message: String, cause: Throwable = null) extends Exceptio } val trimmedLeft = throwable.getStackTrace().view.dropWhile(isBlacklisted) - val trimmedReverse = trimmedLeft.reverse + val trimmedReverse = trimmedLeft.toIndexedSeq.reverse.view .dropWhile(ste => !ste.getClassName.startsWith(builderName)) .dropWhile(isBlacklisted) - trimmedReverse.reverse.toArray + trimmedReverse.toIndexedSeq.reverse.toArray } def chiselStackTrace: String = { diff --git a/core/src/main/scala/chisel3/internal/Namer.scala b/core/src/main/scala/chisel3/internal/Namer.scala index 1694d71d..c6e36cb6 100644 --- a/core/src/main/scala/chisel3/internal/Namer.scala +++ b/core/src/main/scala/chisel3/internal/Namer.scala @@ -8,9 +8,8 @@ import chisel3.experimental.NoChiselNamePrefix import scala.collection.mutable.Stack import scala.collection.mutable.ListBuffer -import scala.collection.JavaConversions._ - import java.util.IdentityHashMap +import scala.collection.JavaConverters._ /** Recursive Function Namer overview * @@ -81,7 +80,14 @@ class NamingContext extends NamingContextInterface { def addDescendant(ref: Any, descendant: NamingContext) { ref match { case ref: AnyRef => - descendants.getOrElseUpdate(ref, ListBuffer[NamingContext]()) += descendant + // getOrElseUpdate + val l = descendants.get(ref) + val buf = if (l != null) l else { + val value = ListBuffer[NamingContext]() + descendants.put(ref, value) + value + } + buf += descendant case _ => anonymousDescendants += descendant } } @@ -111,7 +117,7 @@ class NamingContext extends NamingContextInterface { } } - for (descendant <- descendants.values().flatten) { + for (descendant <- descendants.values.asScala.flatten) { // Where we have a broken naming link, just ignore the missing parts descendant.namePrefix(prefix) } diff --git a/core/src/main/scala/chisel3/package.scala b/core/src/main/scala/chisel3/package.scala index d5a4bfae..64cfa8b9 100644 --- a/core/src/main/scala/chisel3/package.scala +++ b/core/src/main/scala/chisel3/package.scala @@ -207,9 +207,6 @@ package object chisel3 { a.allElements } def getModulePorts(m: Module): Seq[Port] = m.getPorts - // Invalidate API - a DontCare element for explicit assignment to outputs, - // indicating the signal is intentionally not driven. - val DontCare = chisel3.internal.InternalDontCare class BindingException(message: String) extends ChiselException(message) /** A function expected a Chisel type but got a hardware object diff --git a/plugin/src/main/scala-2.12/chisel3/internal/plugin/BundleComponent.scala b/plugin/src/main/scala-2.12/chisel3/internal/plugin/BundleComponent.scala deleted file mode 100644 index 96851e95..00000000 --- a/plugin/src/main/scala-2.12/chisel3/internal/plugin/BundleComponent.scala +++ /dev/null @@ -1,136 +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 - -// TODO This component could also implement val elements in Bundles -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 = { - // 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 - if (scalaVersionOk && arguments.useBundlePlugin) { - unit.body = new MyTypingTransformer(unit).transform(unit.body) - } else { - val reason = if (!scalaVersionOk) { - s"invalid Scala version '${scala.util.Properties.versionNumberString}'" - } else { - s"not enabled via '${arguments.useBundlePluginFullOpt}'" - } - // Enable this with scalacOption '-Ylog:chiselbundlephase' - global.log(s"Skipping BundleComponent on account of $reason.") - } - } - } - - private class MyTypingTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { - - def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe - - val bundleTpe = inferType(tq"chisel3.Bundle") - val dataTpe = inferType(tq"chisel3.Data") - - // Not cached because it should only be run once per class (thus once per Type) - def isBundle(sym: Symbol): Boolean = sym.tpe <:< bundleTpe - - val isDataCache = new mutable.HashMap[Type, Boolean] - // Cached because this is run on every argument to every Bundle - 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 getConstructorAndParams(body: List[Tree]): (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. If you must, override cloneType instead." - global.globalError(d.pos, msg) - case d: DefDef if isNullaryMethodNamed("_usingPlugin", d) => - val msg = "Users cannot override _usingPlugin, it is for the compiler plugin's use only." - global.globalError(d.pos, msg) - case _ => - } - (primaryConstructor, paramAccessors.toList) - } - - - override def transform(tree: Tree): Tree = tree match { - - case bundle: ClassDef if isBundle(bundle.symbol) && !bundle.mods.hasFlag(Flag.ABSTRACT) => - - // ==================== Generate _cloneTypeImpl ==================== - val (con, params) = getConstructorAndParams(bundle.impl.body) - if (con.isEmpty) { - global.reporter.warning(bundle.pos, "Unable to determine primary constructor!") - return super.transform(tree) - } - val constructor = con.get - - val thiz = gen.mkAttributedThis(bundle.symbol) - - // 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. 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. - val select = gen.mkAttributedSelect(thiz, p) - // Clone any Data parameters to avoid field aliasing, need full clone to include direction - if (isData(vp.symbol)) cloneTypeFull(select) else select - }) - - val ttpe = Ident(bundle.symbol) - val neww = localTyper.typed(New(ttpe, conArgs)) - - // Create the symbol for the method and have it be associated with the Bundle class - val cloneTypeSym = bundle.symbol.newMethod(TermName("_cloneTypeImpl"), bundle.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 type to chisel3.Bundle for the override to work - cloneTypeSym.setInfo(NullaryMethodType(bundleTpe)) - - val cloneTypeImpl = localTyper.typed(DefDef(cloneTypeSym, neww)) - - // ==================== Generate _usingPlugin ==================== - // Unclear why quasiquotes work here but didn't for cloneTypeSym, maybe they could. - val usingPlugin = localTyper.typed(q"override protected def _usingPlugin: Boolean = true") - - val withMethods = deriveClassDef(bundle) { t => - deriveTemplate(t)(_ :+ cloneTypeImpl :+ usingPlugin) - } - - super.transform(localTyper.typed(withMethods)) - - case _ => super.transform(tree) - } - } -} diff --git a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselComponent.scala b/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselComponent.scala deleted file mode 100644 index b1302ba3..00000000 --- a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselComponent.scala +++ /dev/null @@ -1,215 +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) 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 = { - // 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('.') - if (scalaVersion(0).toInt == 2 && scalaVersion(1).toInt >= 12) { - 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") - private val shouldMatchDataOrMem : Type => Boolean = shouldMatchGen(tq"chisel3.Data", tq"chisel3.MemBase[_]") - private val shouldMatchModule : Type => Boolean = shouldMatchGen(tq"chisel3.experimental.BaseModule") - - // 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) - // If a Data and in a Bundle, just get the name but not a prefix - if (shouldMatchData(tpe) && 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 (shouldMatchDataOrMem(tpe)) { - val str = stringFromTermName(name) - val newRHS = transform(rhs) - val prefixed = q"chisel3.experimental.prefix.apply[$tpt](name=$str)(f=$newRHS)" - val named = q"chisel3.internal.plugin.autoNameRecursively($str)($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-2.12/chisel3/internal/plugin/ChiselPlugin.scala b/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala deleted file mode 100644 index 23082329..00000000 --- a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala +++ /dev/null @@ -1,39 +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 - -private[plugin] case class ChiselPluginArguments(var useBundlePlugin: Boolean = true) { - def useBundlePluginOpt = "useBundlePlugin" - def useBundlePluginFullOpt = s"-P:chiselplugin:$useBundlePluginOpt" -} - -// The plugin to be run by the Scala compiler during compilation of Chisel code -class ChiselPlugin(val global: Global) extends Plugin { - val name = "chiselplugin" - val description = "Plugin for Chisel 3 Hardware Description Language" - private val arguments = ChiselPluginArguments() - val components: List[PluginComponent] = List[PluginComponent]( - new ChiselComponent(global), - 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 { - error(s"Option not understood: '$option'") - } - } - true - } - - -} - diff --git a/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala b/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala new file mode 100644 index 00000000..96851e95 --- /dev/null +++ b/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala @@ -0,0 +1,136 @@ +// 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 + +// TODO This component could also implement val elements in Bundles +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 = { + // 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 + if (scalaVersionOk && arguments.useBundlePlugin) { + unit.body = new MyTypingTransformer(unit).transform(unit.body) + } else { + val reason = if (!scalaVersionOk) { + s"invalid Scala version '${scala.util.Properties.versionNumberString}'" + } else { + s"not enabled via '${arguments.useBundlePluginFullOpt}'" + } + // Enable this with scalacOption '-Ylog:chiselbundlephase' + global.log(s"Skipping BundleComponent on account of $reason.") + } + } + } + + private class MyTypingTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { + + def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe + + val bundleTpe = inferType(tq"chisel3.Bundle") + val dataTpe = inferType(tq"chisel3.Data") + + // Not cached because it should only be run once per class (thus once per Type) + def isBundle(sym: Symbol): Boolean = sym.tpe <:< bundleTpe + + val isDataCache = new mutable.HashMap[Type, Boolean] + // Cached because this is run on every argument to every Bundle + 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 getConstructorAndParams(body: List[Tree]): (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. If you must, override cloneType instead." + global.globalError(d.pos, msg) + case d: DefDef if isNullaryMethodNamed("_usingPlugin", d) => + val msg = "Users cannot override _usingPlugin, it is for the compiler plugin's use only." + global.globalError(d.pos, msg) + case _ => + } + (primaryConstructor, paramAccessors.toList) + } + + + override def transform(tree: Tree): Tree = tree match { + + case bundle: ClassDef if isBundle(bundle.symbol) && !bundle.mods.hasFlag(Flag.ABSTRACT) => + + // ==================== Generate _cloneTypeImpl ==================== + val (con, params) = getConstructorAndParams(bundle.impl.body) + if (con.isEmpty) { + global.reporter.warning(bundle.pos, "Unable to determine primary constructor!") + return super.transform(tree) + } + val constructor = con.get + + val thiz = gen.mkAttributedThis(bundle.symbol) + + // 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. 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. + val select = gen.mkAttributedSelect(thiz, p) + // Clone any Data parameters to avoid field aliasing, need full clone to include direction + if (isData(vp.symbol)) cloneTypeFull(select) else select + }) + + val ttpe = Ident(bundle.symbol) + val neww = localTyper.typed(New(ttpe, conArgs)) + + // Create the symbol for the method and have it be associated with the Bundle class + val cloneTypeSym = bundle.symbol.newMethod(TermName("_cloneTypeImpl"), bundle.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 type to chisel3.Bundle for the override to work + cloneTypeSym.setInfo(NullaryMethodType(bundleTpe)) + + val cloneTypeImpl = localTyper.typed(DefDef(cloneTypeSym, neww)) + + // ==================== Generate _usingPlugin ==================== + // Unclear why quasiquotes work here but didn't for cloneTypeSym, maybe they could. + val usingPlugin = localTyper.typed(q"override protected def _usingPlugin: Boolean = true") + + val withMethods = deriveClassDef(bundle) { t => + deriveTemplate(t)(_ :+ cloneTypeImpl :+ usingPlugin) + } + + 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 new file mode 100644 index 00000000..b1302ba3 --- /dev/null +++ b/plugin/src/main/scala/chisel3/internal/plugin/ChiselComponent.scala @@ -0,0 +1,215 @@ +// 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) 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 = { + // 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('.') + if (scalaVersion(0).toInt == 2 && scalaVersion(1).toInt >= 12) { + 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") + private val shouldMatchDataOrMem : Type => Boolean = shouldMatchGen(tq"chisel3.Data", tq"chisel3.MemBase[_]") + private val shouldMatchModule : Type => Boolean = shouldMatchGen(tq"chisel3.experimental.BaseModule") + + // 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) + // If a Data and in a Bundle, just get the name but not a prefix + if (shouldMatchData(tpe) && 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 (shouldMatchDataOrMem(tpe)) { + val str = stringFromTermName(name) + val newRHS = transform(rhs) + val prefixed = q"chisel3.experimental.prefix.apply[$tpt](name=$str)(f=$newRHS)" + val named = q"chisel3.internal.plugin.autoNameRecursively($str)($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 new file mode 100644 index 00000000..23082329 --- /dev/null +++ b/plugin/src/main/scala/chisel3/internal/plugin/ChiselPlugin.scala @@ -0,0 +1,39 @@ +// 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 + +private[plugin] case class ChiselPluginArguments(var useBundlePlugin: Boolean = true) { + def useBundlePluginOpt = "useBundlePlugin" + def useBundlePluginFullOpt = s"-P:chiselplugin:$useBundlePluginOpt" +} + +// The plugin to be run by the Scala compiler during compilation of Chisel code +class ChiselPlugin(val global: Global) extends Plugin { + val name = "chiselplugin" + val description = "Plugin for Chisel 3 Hardware Description Language" + private val arguments = ChiselPluginArguments() + val components: List[PluginComponent] = List[PluginComponent]( + new ChiselComponent(global), + 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 { + error(s"Option not understood: '$option'") + } + } + true + } + + +} + diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala index e2689f39..b9ad808b 100644 --- a/src/main/scala/chisel3/aop/Select.scala +++ b/src/main/scala/chisel3/aop/Select.scala @@ -248,7 +248,7 @@ object Select { case other => } }) - predicatedConnects + predicatedConnects.toSeq } /** Selects all stop statements, and includes the predicates surrounding the stop statement @@ -264,7 +264,7 @@ object Select { case other => } }) - stops + stops.toSeq } /** Selects all printf statements, and includes the predicates surrounding the printf statement @@ -280,7 +280,7 @@ object Select { case other => } }) - printfs + printfs.toSeq } // Checks that a module has finished its construction @@ -321,7 +321,7 @@ object Select { } } catch { case e: ChiselException => i.getOptionRef.get match { - case l: LitArg => l.num.intValue().toString + case l: LitArg => l.num.intValue.toString } } diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala index 768680ed..c540fc83 100644 --- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala +++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala @@ -89,7 +89,7 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule]( Seq(other) } - InjectStatement(ModuleTarget(circuit, module.name), ir.Block(stmts), modules, annotations) + InjectStatement(ModuleTarget(circuit, module.name), ir.Block(stmts.toSeq), modules, annotations) }.toSeq } } diff --git a/src/main/scala/chisel3/compatibility.scala b/src/main/scala/chisel3/compatibility.scala index 3b9a3dcc..dde2321d 100644 --- a/src/main/scala/chisel3/compatibility.scala +++ b/src/main/scala/chisel3/compatibility.scala @@ -398,8 +398,8 @@ package object Chisel { } // Deprecated as of Chsiel3 - @throws(classOf[Exception]) object throwException { + @throws(classOf[Exception]) def apply(s: String, t: Throwable = null): Nothing = { val xcpt = new Exception(s, t) throw xcpt diff --git a/src/main/scala/chisel3/util/MixedVec.scala b/src/main/scala/chisel3/util/MixedVec.scala index a632ec3a..14d6be38 100644 --- a/src/main/scala/chisel3/util/MixedVec.scala +++ b/src/main/scala/chisel3/util/MixedVec.scala @@ -91,6 +91,10 @@ final class MixedVec[T <: Data](private val eltsIn: Seq[T]) extends Record with eltsIn.foreach(e => requireIsChiselType(e)) } + // In Scala 2.13, this is protected in IndexedSeq, must override as public because it's public in + // Record + override def className: String = "MixedVec" + // Clone the inputs so that we have our own references. private val elts: IndexedSeq[T] = eltsIn.map(_.cloneTypeFull).toIndexedSeq diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 9503089a..37c4a2b7 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -292,7 +292,7 @@ trait Utils { exceptions.collectFirst{ case a: A => a } match { case Some(a) => throw a case None => exceptions match { - case Nil => Unit + case Nil => () case h :: t => throw h } } diff --git a/src/test/scala/chiselTests/ExtModule.scala b/src/test/scala/chiselTests/ExtModule.scala index 0c3a0633..161b6f5f 100644 --- a/src/test/scala/chiselTests/ExtModule.scala +++ b/src/test/scala/chiselTests/ExtModule.scala @@ -9,7 +9,7 @@ import chisel3.testers.{BasicTester, TesterDriver} // Avoid collisions with regular BlackBox tests by putting ExtModule blackboxes // in their own scope. -package ExtModule { +package extmoduletests { import chisel3.experimental.ExtModule @@ -25,8 +25,8 @@ package ExtModule { } class ExtModuleTester extends BasicTester { - val blackBoxPos = Module(new ExtModule.BlackBoxInverter) - val blackBoxNeg = Module(new ExtModule.BlackBoxInverter) + val blackBoxPos = Module(new extmoduletests.BlackBoxInverter) + val blackBoxNeg = Module(new extmoduletests.BlackBoxInverter) blackBoxPos.in := 1.U blackBoxNeg.in := 0.U @@ -42,10 +42,10 @@ class ExtModuleTester extends BasicTester { */ class MultiExtModuleTester extends BasicTester { - val blackBoxInvPos = Module(new ExtModule.BlackBoxInverter) - val blackBoxInvNeg = Module(new ExtModule.BlackBoxInverter) - val blackBoxPassPos = Module(new ExtModule.BlackBoxPassthrough) - val blackBoxPassNeg = Module(new ExtModule.BlackBoxPassthrough) + val blackBoxInvPos = Module(new extmoduletests.BlackBoxInverter) + val blackBoxInvNeg = Module(new extmoduletests.BlackBoxInverter) + val blackBoxPassPos = Module(new extmoduletests.BlackBoxPassthrough) + val blackBoxPassNeg = Module(new extmoduletests.BlackBoxPassthrough) blackBoxInvPos.in := 1.U blackBoxInvNeg.in := 0.U @@ -71,7 +71,7 @@ class ExtModuleSpec extends ChiselFlatSpec { "DataMirror.modulePorts" should "work with ExtModule" in { ChiselStage.elaborate(new Module { val io = IO(new Bundle { }) - val m = Module(new ExtModule.BlackBoxPassthrough) + val m = Module(new extmoduletests.BlackBoxPassthrough) assert(DataMirror.modulePorts(m) == Seq( "in" -> m.in, "out" -> m.out)) }) diff --git a/src/test/scala/examples/VendingMachineUtils.scala b/src/test/scala/examples/VendingMachineUtils.scala index 131256f8..6847768a 100644 --- a/src/test/scala/examples/VendingMachineUtils.scala +++ b/src/test/scala/examples/VendingMachineUtils.scala @@ -34,6 +34,6 @@ object VendingMachineUtils { value += incValue } } - outputs + outputs.toSeq } } -- cgit v1.2.3