Skip to content

Commit 41db704

Browse files
[Scala3] Add ChiselComponent plugin (#4889)
Initial version of the ChiselComponent naming plugin with 80% of Scala 2 plugin's functionality
1 parent 4a953b3 commit 41db704

File tree

4 files changed

+308
-0
lines changed

4 files changed

+308
-0
lines changed

core/src/main/scala/chisel3/experimental/prefix.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ object prefix {
5555
if (Builder.getPrefix.nonEmpty) Builder.popPrefix()
5656
ret
5757
}
58+
59+
// Used by the Scala 3 plugin ChiselComponent
60+
// TODO(adkian-sifive) This is factored out because the Scala 3
61+
// compiler fails to diambiguate the apply overloads using the type
62+
// parameter
63+
private[chisel3] def applyString[T](name: String)(f: => T): T = {
64+
Builder.pushPrefix(name)
65+
val ret = f
66+
if (Builder.getPrefix.nonEmpty) Builder.popPrefix()
67+
ret
68+
}
5869
}
5970

6071
/** Use to eliminate any existing prefixes within the provided scope.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pluginClass=chisel3.internal.plugin.ChiselComponent
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package chisel3.internal.plugin
4+
5+
import dotty.tools.dotc.*
6+
import dotty.tools.dotc.ast.tpd
7+
import dotty.tools.dotc.ast.tpd.*
8+
import dotty.tools.dotc.ast.Trees
9+
import dotty.tools.dotc.core.Contexts.*
10+
import dotty.tools.dotc.core.Symbols.*
11+
import dotty.tools.dotc.core.Names.TermName
12+
import dotty.tools.dotc.core.StdNames.*
13+
import dotty.tools.dotc.core.Constants.Constant
14+
import dotty.tools.dotc.typer.TyperPhase
15+
import dotty.tools.dotc.plugins.{PluginPhase, StandardPlugin}
16+
import dotty.tools.dotc.transform.{Erasure, Pickler, PostTyper}
17+
import dotty.tools.dotc.core.Types.*
18+
import dotty.tools.dotc.core.Flags
19+
import dotty.tools.dotc.util.SourcePosition
20+
21+
import scala.collection.mutable
22+
23+
class ChiselComponent extends StandardPlugin {
24+
val name: String = "ChiselComponent"
25+
override val description: String = "Chisel's type-specific naming"
26+
27+
override def init(options: List[String]): List[PluginPhase] = {
28+
(new ChiselComponentPhase) :: Nil
29+
}
30+
}
31+
32+
class ChiselComponentPhase extends PluginPhase {
33+
val phaseName: String = "chiselComponentPhase"
34+
override val runsAfter = Set(TyperPhase.name)
35+
36+
private var dataTpe: TypeRef = _
37+
private var memBaseTpe: TypeRef = _
38+
private var verifTpe: TypeRef = _
39+
private var dynObjTpe: TypeRef = _
40+
private var affectsTpe: TypeRef = _
41+
private var moduleTpe: TypeRef = _
42+
private var instTpe: TypeRef = _
43+
private var prefixTpe: TypeRef = _
44+
private var bundleTpe: TypeRef = _
45+
46+
private var pluginModule: TermSymbol = _
47+
private var autoNameMethod: TermSymbol = _
48+
private var withNames: TermSymbol = _
49+
private var prefixModule: TermSymbol = _
50+
private var prefixApplyMethod: TermSymbol = _
51+
52+
override def prepareForUnit(tree: Tree)(using ctx: Context): Context = {
53+
dataTpe = requiredClassRef("chisel3.Data")
54+
memBaseTpe = requiredClassRef("chisel3.MemBase")
55+
verifTpe = requiredClassRef("chisel3.VerificationStatement")
56+
dynObjTpe = requiredClassRef("chisel3.Disable")
57+
affectsTpe = requiredClassRef("chisel3.experimental.AffectsChiselName")
58+
moduleTpe = requiredClassRef("chisel3.experimental.BaseModule")
59+
instTpe = requiredClassRef("chisel3.experimental.hierarchy.Instance")
60+
prefixTpe = requiredClassRef("chisel3.experimental.AffectsChiselPrefix")
61+
bundleTpe = requiredClassRef("chisel3.Bundle")
62+
63+
pluginModule = requiredModule("chisel3")
64+
autoNameMethod = pluginModule.requiredMethod("withName")
65+
withNames = pluginModule.requiredMethod("withNames")
66+
prefixModule = requiredModule("chisel3.experimental.prefix")
67+
prefixApplyMethod = prefixModule.requiredMethod("applyString")
68+
ctx
69+
}
70+
71+
override def transformValDef(tree: tpd.ValDef)(using Context): tpd.Tree = {
72+
val sym = tree.symbol
73+
val tpt = tree.tpt.tpe
74+
val name = sym.name
75+
val rhs = tree.rhs
76+
77+
val valName: String = tree.name.show
78+
val nameLiteral = Literal(Constant(valName))
79+
val prefixLiteral =
80+
if (valName.head == '_')
81+
Literal(Constant(valName.tail))
82+
else Literal(Constant(valName))
83+
84+
val isData = ChiselTypeHelpers.isData(tpt)
85+
val isBoxedData = ChiselTypeHelpers.isBoxedData(tpt)
86+
val isNamedComp = isData || isBoxedData || ChiselTypeHelpers.isNamed(tpt)
87+
val isPrefixed = isNamedComp || ChiselTypeHelpers.isPrefixed(tpt)
88+
89+
if (!ChiselTypeHelpers.okVal(tree)) tree // Cannot name this, so skip
90+
else if (isData && ChiselTypeHelpers.inBundle(tree)) { // Data in a bundle
91+
val newRHS = transformFollowing(rhs)
92+
val named =
93+
tpd
94+
.ref(pluginModule)
95+
.select(autoNameMethod)
96+
.appliedToType(tpt)
97+
.appliedTo(nameLiteral)
98+
.appliedTo(newRHS)
99+
cpy.ValDef(tree)(rhs = named)
100+
} else if (isData || isBoxedData || isPrefixed) { // All Data subtype instances
101+
val newRHS = transformFollowing(rhs)
102+
val prefixed =
103+
tpd
104+
.ref(prefixModule)
105+
.select(prefixApplyMethod)
106+
.appliedToType(tpt)
107+
.appliedTo(prefixLiteral)
108+
.appliedTo(newRHS)
109+
val named = if (isNamedComp) {
110+
tpd
111+
.ref(pluginModule)
112+
.select(autoNameMethod)
113+
.appliedToType(tpt)
114+
.appliedTo(nameLiteral)
115+
.appliedTo(prefixed)
116+
} else prefixed
117+
cpy.ValDef(tree)(rhs = named)
118+
} else if ( // Modules or instances
119+
ChiselTypeHelpers.isModule(tpt) ||
120+
ChiselTypeHelpers.isInstance(tpt)
121+
) {
122+
val newRHS = transformFollowing(rhs)
123+
val named =
124+
tpd
125+
.ref(pluginModule)
126+
.select(autoNameMethod)
127+
.appliedToType(tpt)
128+
.appliedTo(nameLiteral)
129+
.appliedTo(newRHS)
130+
cpy.ValDef(tree)(rhs = named)
131+
} else {
132+
super.transformValDef(tree)
133+
}
134+
}
135+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package chisel3.internal.plugin
4+
5+
import scala.collection.mutable
6+
import dotty.tools.dotc.ast.Trees.*
7+
import dotty.tools.dotc.core.Types.*
8+
import dotty.tools.dotc.core.Flags
9+
import dotty.tools.dotc.core.Symbols.*
10+
import dotty.tools.dotc.core.Constants.*
11+
import dotty.tools.dotc.core.Contexts.*
12+
import dotty.tools.dotc.core.Names.TermName
13+
import dotty.tools.dotc.ast.tpd
14+
15+
object ChiselTypeHelpers {
16+
17+
val badFlagsVal = Set(
18+
Flags.Param,
19+
Flags.Synthetic,
20+
Flags.Deferred,
21+
Flags.CaseAccessor,
22+
Flags.ParamAccessor
23+
)
24+
25+
val badFlagsUnapply = Set(
26+
Flags.Param,
27+
Flags.Deferred,
28+
Flags.CaseAccessor,
29+
Flags.ParamAccessor
30+
)
31+
32+
val goodFlagsUnapply = Set(Flags.Synthetic, Flags.Artifact)
33+
34+
def okVal(dd: tpd.ValDef)(using Context): Boolean = {
35+
val modsOk = badFlagsVal.forall(f => !dd.symbol.flags.is(f))
36+
val isNull = dd.rhs match {
37+
case Literal(Constant(null)) => true
38+
case _ => false
39+
}
40+
modsOk && !isNull && !dd.rhs.isEmpty
41+
}
42+
43+
def okUnapply(dd: tpd.ValDef)(using Context): Boolean = {
44+
val flagsOk =
45+
goodFlagsUnapply.forall(f => dd.symbol.flags.is(f))
46+
&& badFlagsUnapply.forall(f => !dd.symbol.flags.is(f))
47+
val isNull = dd.rhs match {
48+
case Literal(Constant(null)) => true
49+
case _ => false
50+
}
51+
52+
val tpe = dd.tpt.tpe
53+
54+
tpe.typeSymbol.fullName.startsWith("Tuple")
55+
&& flagsOk
56+
&& !isNull
57+
&& !dd.rhs.isEmpty
58+
}
59+
60+
def findUnapplyNames(tree: Tree[?]): Option[List[String]] = {
61+
val applyArgs = tree match {
62+
case Match(_, List(CaseDef(_, _, Apply(_, args)))) => Some(args)
63+
case _ => None
64+
}
65+
applyArgs.flatMap { args =>
66+
var ok = true
67+
val result = mutable.ListBuffer[String]()
68+
args.foreach {
69+
case x: Ident[?] => result += x.name.toString
70+
case _ => ok = false
71+
}
72+
if (ok) Some(result.toList) else None
73+
}
74+
}
75+
76+
def inBundle(dd: tpd.ValDef)(using Context): Boolean = {
77+
dd.symbol.owner.owner.thisType <:< requiredClassRef("chisel3.Bundle")
78+
}
79+
80+
def stringFromTermName(name: TermName): String = name.toString.trim
81+
82+
def isData(t: Type)(using Context): Boolean = {
83+
val dataTpe = getClassIfDefined("chisel3.Data")
84+
t.baseClasses.contains(dataTpe)
85+
}
86+
87+
def isBoxedData(tpe: Type)(using Context): Boolean = {
88+
val optionClass = getClassIfDefined("scala.Option")
89+
val iterableClass = getClassIfDefined("scala.collection.Iterable")
90+
91+
// Recursion here is needed only for nested iterables. These
92+
// nested iterable may or may not have Data in their leaves.
93+
def rec(tpe: Type): Boolean = {
94+
tpe match {
95+
case AppliedType(tycon, List(arg)) =>
96+
tycon match {
97+
case tp: TypeRef =>
98+
val isIterable = tp.symbol.derivesFrom(iterableClass)
99+
val isOption = tp.symbol == optionClass
100+
101+
(isOption, isIterable, isData(arg)) match {
102+
case (true, false, true) => true // Option
103+
case (false, true, true) => true // Iterable with Data
104+
case (false, true, false) => rec(arg) // Possibly nested iterable
105+
case _ => false
106+
}
107+
case _ =>
108+
// anonymous subtype of Iterable,
109+
// or AppliedType from higher-kinded type
110+
rec(arg)
111+
}
112+
113+
case tr: TypeRef =>
114+
// Follow abstract type aliases like trait Seq[A] by looking at its info
115+
tr.info match {
116+
case TypeBounds(lo, hi) if lo != hi =>
117+
// Try upper bound if available
118+
rec(hi)
119+
case TypeAlias(alias) =>
120+
rec(alias)
121+
case _ =>
122+
false
123+
}
124+
case _ =>
125+
false
126+
}
127+
}
128+
rec(tpe)
129+
}
130+
131+
def isNamed(t: Type)(using Context): Boolean = {
132+
val dataTpe = getClassIfDefined("chisel3.Data")
133+
val memBaseTpe = getClassIfDefined("chisel3.MemBase")
134+
val verifTpe = getClassIfDefined("chisel3.VerificationStatement")
135+
val disableTpe = getClassIfDefined("chisel3.Disable")
136+
val affectsTpe = getClassIfDefined("chisel3.experimental.AffectsChiselName")
137+
val dynObjTpe = getClassIfDefined("chisel3.properties.DynamicObject")
138+
139+
t.baseClasses.contains(dataTpe)
140+
|| t.baseClasses.contains(memBaseTpe)
141+
|| t.baseClasses.contains(verifTpe)
142+
|| t.baseClasses.contains(disableTpe)
143+
|| t.baseClasses.contains(affectsTpe)
144+
|| t.baseClasses.contains(dynObjTpe)
145+
}
146+
147+
def isModule(t: Type)(using Context): Boolean = {
148+
val baseModuleTpe = getClassIfDefined("chisel3.experimental.BaseModule")
149+
t.baseClasses.contains(baseModuleTpe)
150+
}
151+
152+
def isInstance(t: Type)(using Context): Boolean = {
153+
val instanceTpe = getClassIfDefined("chisel3.experimental.hierarchy.Instance")
154+
t.baseClasses.contains(instanceTpe)
155+
}
156+
157+
def isPrefixed(t: Type)(using Context): Boolean = {
158+
val affectsTpe = getClassIfDefined("chisel3.experimental.AffectsChiselName")
159+
t.baseClasses.contains(affectsTpe)
160+
}
161+
}

0 commit comments

Comments
 (0)