The following macro, extracted from a larger example, is supposed to create a tree with nothing but a reference to this:
def echoThisImpl(c:Context): c.Expr[Any] = {
import c.universe._
val selfTree = This(c.enclosingClass.symbol)
c.Expr[AnyRef](selfTree)
}
def echoThis: Any = macro CallMacro.echoThisImpl
But a call to echoThis such as
object Testing extends App {
val thisValue = CallMacro.echoThis
println(thisValue)
}
fails to compile, with the message
[error] /home/rafael/dev/scala/goose/goose-macros/src/test/scala/Testing.scala:8: type mismatch;
[error] found : <noprefix>
[error] required: Any
[error] val thisValue = CallMacro.echoThis
If I set the -Ymacro-debug-lite flag the generated tree is This(newTermName("<local Testing>")).
There are two options of achieving what you want:
1) Use This(tpnme.EMPTY). Currently this doesn't compile, so you'll have to use This(newTypeName("")) instead, but in RC1 this will be fixed.
2) Use This(c.enclosingClass.symbol.asModule.moduleClass). Currently this doesn't work, because of https://issues.scala-lang.org/browse/SI-6394, but in RC1 this will be fixed.
Related
I'm using sbt to build some of the riscv boom from the source code, but the sbt complains that it "could not find implicit value for parameter valName: : freechips.rocketchip.diplomacy.ValName". The detailed error message are as below:
[error] F:\hiMCU\my_proj\src\main\scala\freechips\rocketchip\tile\BaseTile.scala:170:42: could not find implicit value for parameter valName: freechips.rocketchip.diplomacy.ValName
[error] Error occurred in an application involving default arguments.
[error] protected val tlMasterXbar = LazyModule(new TLXbar)
The code where sbt complains is as below:
abstract class BaseTile private (val crossing: ClockCrossingType, q: Parameters)
extends LazyModule()(q)
with CrossesToOnlyOneClockDomain
with HasNonDiplomaticTileParameters
{
// Public constructor alters Parameters to supply some legacy compatibility keys
def this(tileParams: TileParams, crossing: ClockCrossingType, lookup: LookupByHartIdImpl, p: Parameters) = {
this(crossing, p.alterMap(Map(
TileKey -> tileParams,
TileVisibilityNodeKey -> TLEphemeralNode()(ValName("tile_master")),
LookupByHartId -> lookup
)))
}
def module: BaseTileModuleImp[BaseTile]
def masterNode: TLOutwardNode
def slaveNode: TLInwardNode
def intInwardNode: IntInwardNode // Interrupts to the core from external devices
def intOutwardNode: IntOutwardNode // Interrupts from tile-internal devices (e.g. BEU)
def haltNode: IntOutwardNode // Unrecoverable error has occurred; suggest reset
def ceaseNode: IntOutwardNode // Tile has ceased to retire instructions
def wfiNode: IntOutwardNode // Tile is waiting for an interrupt
protected val tlOtherMastersNode = TLIdentityNode()
protected val tlSlaveXbar = LazyModule(new TLXbar)
protected val tlMasterXbar = LazyModule(new TLXbar)
protected val intXbar = LazyModule(new IntXbar)
....
}
The LazyModule object code is as below:
object LazyModule
{
protected[diplomacy] var scope: Option[LazyModule] = None
private var index = 0
def apply[T <: LazyModule](bc: T)(implicit valName: ValName, sourceInfo: SourceInfo): T = {
// Make sure the user put LazyModule around modules in the correct order
// If this require fails, probably some grandchild was missing a LazyModule
// ... or you applied LazyModule twice
require (scope.isDefined, s"LazyModule() applied to ${bc.name} twice ${sourceLine(sourceInfo)}")
require (scope.get eq bc, s"LazyModule() applied to ${bc.name} before ${scope.get.name} ${sourceLine(sourceInfo)}")
scope = bc.parent
bc.info = sourceInfo
if (!bc.suggestedNameVar.isDefined) bc.suggestName(valName.name)
bc
}
}
I think the sbt should find some val of type freechips.rocketchip.diplomacy.ValName, but it didn't find such kind of val.
You need to have an object of type ValName in the scope where your LazyModules are instantiated:
implicit val valName = ValName("MyXbars")
For more details on Scala's implicit please see https://docs.scala-lang.org/tutorials/tour/implicit-parameters.html.html
You generally shouldn't need to manually create a ValName, the Scala compiler can materialize them automatically based on the name of the val you're assigning the LazyModule to. You didn't include your imports in your example, but can you try importing ValName?
import freechips.rocketchip.diplomacy.ValName
In most of rocket-chip code, this is imported via wildcard importing everything in the diplomacy package
import freechips.rocketchip.diplomacy._
OUTLINE
I have an API that looks something like this:
package com.example
object ExternalApi {
def create[T <: SpecialElement](elem: T): TypeConstructor[T] =
TypeConstructor(elem)
def create1[T <: SpecialElement](elem: T): TypeConstructor[T] =
TypeConstructor(elem)
def create2[T <: SpecialElement](elem: T): TypeConstructor[T] =
TypeConstructor(elem)
//...
}
object MyApi {
def process[T <: TypeConstructor[_ <: SpecialElement]](
l: T,
metadata: List[String]): T = {
println("I've been called!")
//do some interesting stuff with the List's type parameter here
l
}
}
case class TypeConstructor[E](elem: E)
trait SpecialElement
The ExternalApi (which is actually external to my lib, so no modifying that) has a series of calls that I'd like to automatically wrap with MyApi.process calls, with the metadata argument derived from the final type of T.
To illustrate, the calls to be wrapped could have any form, including nested calls, and calls within other AST subtree types (such as Blocks), e.g. :
package com.example.test
import com.example.{ExternalApi, SpecialElement}
object ApiPluginTest extends App {
//one possible form
val targetList = ExternalApi.create(Blah("name"))
//and another
ExternalApi.create2(ExternalApi.create1(Blah("sth")).elem)
//and yet another
val t = {
val sub1 = ExternalApi.create(Blah("anything"))
val sub2 = ExternalApi.create1(sub1.elem)
sub2
}
}
case class Blah(name: String) extends SpecialElement
Since compiler plugins handle matching structures within ASTs recursively "for free", I've decided to go with them.
However, due to the fact that I need to match a specific type signature, the plugin follows the typer phase.
Here's the code of the PluginComponent:
package com.example.plugin
import com.example.{SpecialElement, TypeConstructor}
import scala.tools.nsc.Global
import scala.tools.nsc.plugins.PluginComponent
import scala.tools.nsc.transform.Transform
class WrapInApiCallComponent(val global: Global)
extends PluginComponent
with Transform {
protected def newTransformer(unit: global.CompilationUnit) =
WrapInApiCallTransformer
val runsAfter: List[String] = List("typer") //since we need the type
val phaseName: String = WrapInApiCallComponent.Name
import global._
object WrapInApiCallTransformer extends Transformer {
override def transform(tree: global.Tree) = {
val transformed = super.transform(tree)
transformed match {
case call # Apply(_, _) =>
if (call.tpe != null && call.tpe.finalResultType <:< typeOf[
TypeConstructor[_ <: SpecialElement]]) {
println(s"Found relevant call $call")
val typeArguments = call.tpe.typeArgs.map(_.toString).toList
val listSymbOf = symbolOf[List.type]
val wrappedFuncSecondArgument =
q"$listSymbOf.apply(..$typeArguments)"
val apiObjSymbol = symbolOf[com.example.MyApi.type]
val wrappedCall =
q"$apiObjSymbol.process[${call.tpe.finalResultType}]($call, $wrappedFuncSecondArgument)"
//explicit typing, otherwise later phases throw NPEs etc.
val ret = typer.typed(wrappedCall)
println(showRaw(ret))
println("----")
ret
} else {
call
}
case _ => transformed
}
}
}
}
object WrapInApiCallComponent {
val Name = "api_embed_component"
}
This seems to resolve identifiers, as well as types, correctly, outputting e.g.:
Apply(TypeApply(Select(TypeTree().setOriginal(Ident(com.example.MyApi)), TermName("process")), List(TypeTree())), List(Apply(TypeApply(Select(Select(Select(Ident(com), com.example), com.example.MyApi), TermName("create")), List(TypeTree())), List(Apply(Select(Ident(com.example.test.Blah), TermName("apply")), List(Literal(Constant("name")))))), Apply(TypeApply(Select(TypeTree().setOriginal(Ident(scala.collection.immutable.List)), TermName("apply")), List(TypeTree())), List(Literal(Constant("com.example.test.Blah"))))))
Unfortunately, I get an error during compilation starting with:
scala.reflect.internal.FatalError:
[error]
[error] Unexpected tree in genLoad: com.example.MyApi.type/class scala.reflect.internal.Trees$TypeTree at: RangePosition([projectpath]/testPluginAutoWrap/compiler_plugin_test/src/main/scala/com/example/test/ApiPluginTest.scala, 108, 112, 112)
[error] while compiling: [projectpath]/testPluginAutoWrap/compiler_plugin_test/src/main/scala/com/example/test/ApiPluginTest.scala
[error] during phase: jvm
[error] library version: version 2.12.4
[error] compiler version: version 2.12.4
[error] reconstructed args: -Xlog-implicits -classpath [classpath here]
[error]
[error] last tree to typer: TypeTree(class String)
[error] tree position: line 23 of [projectpath]/testPluginAutoWrap/compiler_plugin_test/src/main/scala/com/example/test/ApiPluginTest.scala
QUESTION
It looks I'm screwing something up with type definitions, but what is it?
Specifically:
How do I correctly wrap every ExternalApi.createX call with an MyApi.process call, constrained by the requirements provided above?
NOTES
Given the amount of boilerplate required, I've set up a complete example project. It's available here.
The answer does not have to define a compiler plugin. If you're able to cover all the relevant calls with a macro, that is fine as well.
Originally the wrapped call was to something like: def process[T <: TypeConstructor[_ <: SpecialElement] : TypeTag](l: T): T, the setup here is actually a workaround. So if you are able to generate a wrapped call of this type, i.e. one that includes a runtime TypeTag[T], that's fine as well.
I'm having trouble figuring out how to update a column with type enumeration using play-slick.
Here's my enum and case class:
object TestStatus extends Enumeration {
type TestStatus = Value
val Status1 = Value("Status1")
}
case class Test (
id: String,
status: TestStatus
)
and the table mapping:
class Tests(tag: Tag) extends Table[Test](tag, "tests") {
implicit val statusColumn = MappedColumnType.base[TestStatus, String](_.toString, TestStatus.withName)
override def * = (id, status) <> ((Test.apply _).tupled, Test.unapply)
val id = column[String]("id", 0.PrimaryKey)
val status = column[TestStatus]("status")
}
when I try to go and update a Tests row, I get an error:
object TestQueries extends TableQuery[Tests](new Tests(_)) {
def updateStatus(id: String, newStatus: TestStatus) = {
TestQueries.filter(_.id === id).map(_.status).update(newStatus)
}
}
[error] Slick does not know how to map the given types.
[error] Possible causes: T in Table[T] does not match your * projection,
[error] you use an unsupported type in a Query (e.g. scala List),
[error] or you forgot to import a driver api into scope.
[error] Required level: slick.lifted.FlatShapeLevel
[error] Source type: slick.lifted.Rep[models.TestStatus.Value]
[error] Unpacked type: T
[error] Packed type: G
[error] TestQueries.filter(_.id === id).map(_.status).update(newStatus)
[error] ^
IntelliJ is showing that TestQueries.filter(_.id === id).map(_.status) has type Query[Nothing, Nothing, Seq], which makes me think the problem is with the specific column rather than with the update function.
Updating the id works fine using the same structure.
You need to define the custom column type of TestStatus.Value. This is how slick allows you to build a custom column type by mapping it to an already supported type:
implicit def testStatCT: BaseTypedType[TestStatus.Value] =
MappedColumnType.base[TestStatus.Value, String](
enum => enum.toString, str => TestStatus.withName(str)
)
this implicit needs to be imported wherever implicit resolutions such as the one in your example fail (or better yet defined in the TestStatus object so that it's always available) this way slick can have evidence that TestStatus.Value is a BaseTypedType which basically just means that something is a supported column type.
For further read on column mapping you can look at Slick Documentation
Background
I'm trying to add a #Prisms annotation to Monocle that will work as follows. Given:
#Prisms sealed trait Foo
case object A extends Foo
case object B extends Foo
it will generate a companion object for Foo:
object Foo {
val a = monocle.macros.GenPrism.apply[Foo, A]
val b = monocle.macros.GenPrism.apply[Foo, B]
}
(or if the companion object already exists, it will add those methods to it).
The macro relies on directKnownSubclasses to find A and B, so there will be a note in the Monocle readme recommending that people use Typelevel Scala. I've configured Monocle to use TLS on my branch.
First attempt
I tried using TypeNames to represent the parent type Foo and child type (A or B):
def findSubclasses(typeName: TypeName): Set[TypeName] = {
// Note: disable macros to prevent stack overflow caused by infinite typing loop!
val tpe = c.typecheck(Ident(typeName), mode = c.TYPEmode, silent = true, withMacrosDisabled = true)
tpe.symbol.asClass.knownDirectSubclasses.map(_.asType.name)
}
def prisms(parentTypename: TypeName, childNames: Set[TypeName]): Set[Tree] = childNames.map { childName =>
val prismName = TermName(prefix + childName.decodedName.toString.toLowerCase)
q"""val $prismName = monocle.macros.GenPrism.apply[$parentTypename, $childName]"""
}
This generates what looks like reasonable code:
{
sealed trait Foo;
object Foo {
val a = monocle.macros.GenPrism.apply[Foo, A];
val b = monocle.macros.GenPrism.apply[Foo, B]
};
()
}
But it doesn't seem to work:
[error] /Users/chris/code/Monocle/test/shared/src/test/scala/other/PrismsAnnotationSpec.scala:5: not found: type A
[error] #monocle.macros.Prisms sealed trait Foo
[error] ^
[error] one error found
Second attempt
I also tried using TypeSymbols instead of TypeNames:
def prisms(parentTypename: TypeName, childSymbols: Set[TypeSymbol]): Set[Tree] = {
val parentSymbol: TypeSymbol =
c.typecheck(Ident(parentTypename), mode = c.TYPEmode, silent = true, withMacrosDisabled = true)
.symbol
.asType
childSymbols.map { childSymbol =>
val prismName = TermName(prefix + childSymbol.name.decodedName.toString.toLowerCase)
q"""val $prismName = monocle.macros.GenPrism.apply[$parentSymbol, $childSymbol]"""
}
}
But this gives me the following error, so I'm guessing the generated tree is invalid:
[error] /Users/chris/code/Monocle/test/shared/src/test/scala/other/PrismsAnnotationSpec.scala:5: macro annotation could not be expanded (the most common reason for that is that you need to enable the macro paradise plugin; another possibility is that you try to use macro annotation in the same compilation run that defines it)
[error] #monocle.macros.Prisms sealed trait Foo
[error] ^
[error] one error found
Third attempt
I also tried turning the TypeSymbols into Types:
def prisms(parentTypename: TypeName, childSymbols: Set[TypeSymbol]): Set[Tree] = {
val parentSymbol: TypeSymbol =
c.typecheck(Ident(parentTypename), mode = c.TYPEmode, silent = true, withMacrosDisabled = true)
.symbol
.asType
childSymbols.map { childSymbol =>
val prismName = TermName(prefix + childSymbol.name.decodedName.toString.toLowerCase)
q"""val $prismName = monocle.macros.GenPrism.apply[${parentSymbol.toType}, ${childSymbol.toType}]"""
}
}
But I get the same error, saying the annotation could not be expanded.
Now I'm out of ideas.
How to reproduce
The complete code is available on this branch: https://github.com/cb372/Monocle/tree/sealed-trait-macro-annotation.
First attempt
Second attempt
Third attempt
The macro is used in test/shared/src/test/scala/other/PrismsAnnotationSpec.scala. To compile it, run sbt test:compile.
I am trying to call a macro from a macro, but I'm doing something wrong. It looks approximately like this:
import play.api.libs.json._
import scala.reflect.macros.Context
import language.experimental.macros
object Extension {
def apply[A]: Format[A] = macro applyImpl[A]
def applyImpl[A: c.WeakTypeTag](c: Context): c.Expr[Format[A]] = {
import c.universe._
val aTpeW = c.weakTypeOf[A]
val aClazz = aTpeW.typeSymbol.asClass
if (!aClazz.isSealed) { // fall back to Json.format
val t = reify { Json.format[A] } .tree
return c.Expr[Format[A]](t)
}
???
}
}
In other words, based on some condition of the type of A, instead of generating a tree in my macro, I want to return the body of another macro (Json.format). But somehow this gets expanded already before using the macro. When I compile this, I get
[error] .../Extension.scala:47: No unapply function found
[error] val t = reify { Json.format[A] } .tree
[error] ^
Which means that format is already executed (it should not be). The format method is defined as
def format[A] = macro JsMacroImpl.formatImpl[A]
One needs to jump right into the macro body it seems:
if (!aClazz.isSealed) { // fall back to Json.format
return JsMacroImpl.formatImpl[A](c)
}
(IntelliJ had this red, so I thought it was wrong, but it actually compiles)
Alternatively you should be able to call the macro from a macro, when you put both macros in different compilation units (i.e. different projects). Scala cannot compile a macro and apply it in the same compilation run.