// SPDX-License-Identifier: Apache-2.0 package chisel3.internal import scala.language.experimental.macros import scala.annotation.StaticAnnotation import scala.reflect.macros.whitebox private[chisel3] object instantiableMacro { def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { import c.universe._ def processBody(stats: Seq[Tree]): (Seq[Tree], Iterable[Tree]) = { val extensions = scala.collection.mutable.ArrayBuffer.empty[Tree] extensions += q"implicit val mg = new chisel3.internal.MacroGenerated{}" // Note the triple `_` prefixing `module` is to avoid conflicts if a user marks a 'val module' // with @public; in this case, the lookup code is ambiguous between the generated `def module` // function and the argument to the generated implicit class. val resultStats = stats.flatMap { stat => stat match { case hasPublic: ValOrDefDef if hasPublic.mods.annotations.toString.contains("new public()") => hasPublic match { case aDef: DefDef => c.error(aDef.pos, s"Cannot mark a def as @public") Nil // For now, we only omit protected/private vals case aVal: ValDef if aVal.mods.hasFlag(c.universe.Flag.PRIVATE) || aVal.mods.hasFlag(c.universe.Flag.PROTECTED) => c.error(aVal.pos, s"Cannot mark a private or protected val as @public") Nil case aVal: ValDef => extensions += atPos(aVal.pos)(q"def ${aVal.name} = ___module._lookup(_.${aVal.name})") if (aVal.name.toString == aVal.children.last.toString) Nil else Seq(aVal) case other => Seq(other) } case other => Seq(other) } } (resultStats, extensions) } val result = { val (clz, objOpt) = annottees.map(_.tree).toList match { case Seq(c, o) => (c, Some(o)) case Seq(c) => (c, None) } val (newClz, implicitClzs, tpname) = clz match { case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" => val defname = TypeName(tpname + c.freshName()) val instname = TypeName(tpname + c.freshName()) val (newStats, extensions) = processBody(stats) val argTParams = tparams.map(_.name) ( q""" $mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents with chisel3.experimental.hierarchy.IsInstantiable { $self => ..$newStats } """, Seq( q"""implicit class $defname[..$tparams](___module: chisel3.experimental.hierarchy.Definition[$tpname[..$argTParams]]) { ..$extensions }""", q"""implicit class $instname[..$tparams](___module: chisel3.experimental.hierarchy.Instance[$tpname[..$argTParams]]) { ..$extensions } """ ), tpname ) case q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }" => val defname = TypeName(tpname + c.freshName()) val instname = TypeName(tpname + c.freshName()) val (newStats, extensions) = processBody(stats) val argTParams = tparams.map(_.name) ( q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents with chisel3.experimental.hierarchy.IsInstantiable { $self => ..$newStats }", Seq( q"""implicit class $defname[..$tparams](___module: chisel3.experimental.hierarchy.Definition[$tpname[..$argTParams]]) { ..$extensions }""", q"""implicit class $instname[..$tparams](___module: chisel3.experimental.hierarchy.Instance[$tpname[..$argTParams]]) { ..$extensions } """ ), tpname ) } val newObj = objOpt match { case None => q"""object ${tpname.toTermName} { ..$implicitClzs } """ case Some(obj @ q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }") => q""" $mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$implicitClzs ..$body } """ } q""" $newClz $newObj """ } c.Expr[Any](result) } } private[chisel3] class instantiable extends StaticAnnotation { def macroTransform(annottees: Any*): Any = macro instantiableMacro.impl } private[chisel3] class public extends StaticAnnotation