Description
Compiler version
Error happens in 3.2.0 as well as latest nightly 3.2.2-RC1-bin-20221006-a6a3385-NIGHTLY
.
Minimized code
Let's create a context object that has an encoder instance:
import quotes.reflect._
trait MyEncoder[T]:
def encode: String
class Context:
inline def summonMyEncoder[T]: String =
${ SummonEncoder.impl[T] }
implicit val encoderInstance: MyEncoder[String] =
new MyEncoder[String] { def encode = "blah" }
end Context
Then let's make the macro SummonEncoder.impl
summon that instance:
import scala.quoted._
object SummonEncoder:
def impl[T: Type](using Quotes) =
import quotes.reflect._
Expr.summon[MyEncoder[T]] match
case Some(enc) => '{ $enc.encode }
case None => report.throwError("can't do it")
Then let's create a 'Repo' object that internally uses the context to summon an encoder for a particular type,
...and then use that summoning function.
class Repo[T]:
val ctx = new Context
inline def summonEncoder = { import ctx._ // change to: import ctx.{given, _} for the given example
summonMyEncoder[T]
}
object Use:
val repo = new Repo[String]
val v = repo.summonEncoder // error happens here!
Output
Long exception while typing Repo.this...
error happens:
exception while typing Repo.this of class class dotty.tools.dotc.ast.Trees$This # -1
exception while typing Repo.this.ctx of class class dotty.tools.dotc.ast.Trees$Select # -1
exception while typing Repo.this.ctx.encoderInstance of class class dotty.tools.dotc.ast.Trees$Select # -1
exception while typing Repo.this.ctx.encoderInstance of class class dotty.tools.dotc.ast.Trees$Inlined # -1
exception while typing Repo.this.ctx.encoderInstance.encode of class class dotty.tools.dotc.ast.Trees$Select # -1
exception while typing Repo.this.ctx.encoderInstance.encode of class class dotty.tools.dotc.ast.Trees$Inlined # -1
exception while typing Repo.this.ctx.encoderInstance.encode:String of class class dotty.tools.dotc.ast.Trees$Typed # -1
exception while typing Repo.this.ctx.encoderInstance.encode:String of class class dotty.tools.dotc.ast.Trees$Inlined # -1
exception while typing {
Repo.this.ctx.encoderInstance.encode:String
} of class class dotty.tools.dotc.ast.Trees$Block # -1
exception while typing {
Repo.this.ctx.encoderInstance.encode:String
}:String of class class dotty.tools.dotc.ast.Trees$Typed # -1
exception while typing {
val Repo_this: (org.deusaquilus.Use.repo : (): org.deusaquilus.Repo) =
org.deusaquilus.Use.repo
{
Repo.this.ctx.encoderInstance.encode:String
}:String
} of class class dotty.tools.dotc.ast.Trees$Inlined # -1
exception while typing def v: String =
{
val Repo_this: (org.deusaquilus.Use.repo : (): org.deusaquilus.Repo) =
org.deusaquilus.Use.repo
{
Repo.this.ctx.encoderInstance.encode:String
}:String
} of class class dotty.tools.dotc.ast.Trees$DefDef # -1
exception while typing @SourceFile("src/main/scala/org/deusaquilus/Use.scala") final module class Use()
extends
Object() {
private def writeReplace(): AnyRef =
new scala.runtime.ModuleSerializationProxy(classOf[org.deusaquilus.Use.type]
)
def repo: org.deusaquilus.Repo[String] = new org.deusaquilus.Repo[String]()
def v: String =
{
val Repo_this: (org.deusaquilus.Use.repo : (): org.deusaquilus.Repo) =
org.deusaquilus.Use.repo
{
Repo.this.ctx.encoderInstance.encode:String
}:String
}
} of class class dotty.tools.dotc.ast.Trees$TypeDef # -1
exception while typing package org.deusaquilus {
@SourceFile("src/main/scala/org/deusaquilus/Use.scala") class Repo[T]()
extends
Object() {
private type T
def ctx: org.deusaquilus.Context = new org.deusaquilus.Context()
private inline def summonEncoder: String =
scala.compiletime.package$package.erasedValue[String]
}
final lazy module val Use: org.deusaquilus.Use = new org.deusaquilus.Use()
@SourceFile("src/main/scala/org/deusaquilus/Use.scala") final module class Use
()
extends Object() {
private def writeReplace(): AnyRef =
new scala.runtime.ModuleSerializationProxy(
classOf[org.deusaquilus.Use.type]
)
def repo: org.deusaquilus.Repo[String] = new org.deusaquilus.Repo[String]()
def v: String =
{
val Repo_this: (org.deusaquilus.Use.repo : (): org.deusaquilus.Repo) =
org.deusaquilus.Use.repo
{
Repo.this.ctx.encoderInstance.encode:String
}:String
}
}
} of class class dotty.tools.dotc.ast.Trees$PackageDef # -1
[info] exception occurred while compiling /home/alexi/git/encoder-typing-reproduction/src/main/scala/org/deusaquilus/Use.scala
java.lang.AssertionError: assertion failed: asTerm called on not-a-Term val <none> while compiling /home/alexi/git/encoder-typing-reproduction/src/main/scala/org/deusaquilus/Use.scala
[error] ## Exception when compiling 3 sources to /home/alexi/git/encoder-typing-reproduction/target/scala-3.2.0/classes
[error] java.lang.AssertionError: assertion failed: asTerm called on not-a-Term val <none>
[error] scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
[error] dotty.tools.dotc.core.Symbols$Symbol.asTerm(Symbols.scala:169)
Expectation
It should work and summon the encoder property.
Github Example:
You can find an example of the codebase here:
https://github.com/deusaquilus/encoder-typing-reproduction
The exact same problem happens if you change the encoder to a given
and change the import to import ctx.{given, _}
.
Why this is Important
I would really like to use Scala 3 inline to be able to create DAO repository patterns. That would look something like this:
// Some arbitrary class
case class Person(id: Int, name: String, age: Int)
// Outline of a generic DAO repository
class Repo[T <: { def id: Int }](val ctx: PostgresJdbcContext[Literal]) {
inline def getById(inline id: Int): Option[T] = { import ctx._
run(query[T].filter(t => t.id == lift(id))).headOption
}
inline def insert(inline t: T): Int = { import ctx._
run(query[T].insertValue(lift(t)).returning(_.id))
}
inline def searchByField(inline predicate: T => Boolean) = { import ctx._
run(query[T].filter(p => predicate(p)))
}
}
// Specialize the repo for a particular class
class PeopleRepo(val ctx: PostgresJdbcContext[Literal]) extends Repo[Person](myContext)
// Declare and use it:
val peopleRepo = new PeopleRepo("testPostgresDB")
val joe = Person(123, "Joe", 123)
val joeId = peopleRepo.insert(joe)
val joeNew = peopleRepo.getById(joeId)
val allJoes = peopleRepo.searchByField(p => p.name == "Joe")
Only I can't do this because the above error will happen in my encoders and decoders.