Skip to content

Typing error when inline used together with summons and implicits in imported class #16156

Open
@deusaquilus

Description

@deusaquilus

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.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions