-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Referring to outer class in implicit inline breaks Expr summoning mechanism #12179
Comments
Btw, I cannot create AnyVal encoders in Quill unless this is solved 😧 |
@deusaquilus a side question: can't you use |
I can make an Mapped-Encoder for opaque types but whatever macros I use for that will probably have a similar issue as this one. |
Minimized to import scala.quoted.*
class Encoder
class EncoderContext:
implicit inline def anyClsEncoder: Encoder =
${ EncoderContext.applyImpl('this) }
object EncoderContext:
def applyImpl(ctx: Expr[EncoderContext])(using Quotes): Expr[Encoder] =
'{ $ctx; ??? }
object SummonAndEncode:
inline def apply(): Unit =
${ applyImpl }
private def applyImpl(using Quotes): Expr[Unit] =
Expr.summon[Encoder] match
case Some(value) => println("ENCODER FOUND: " + value.show)
case None => quotes.reflect.report.error("ENCODER NOT FOUND: " + Type.show[Encoder])
'{ () } object Test:
def main(args: Array[String]): Unit = {
val ctx = new EncoderContext()
import ctx._
summon[Encoder] // ok
SummonAndEncode() // breaks
} |
Possible workaround class EncoderContext { self =>
def encode[Cls](cls: Cls) = List(cls.toString)
implicit inline def anyClsEncoder[Cls]: Encoder[Cls] =
- MappedEncoderMaker[Cls](self)
+ new Encoder[Cls] { def encode(m: Cls) = self.encode[Cls](m) }
} |
Unfortunately, that workaround doesn't work for me. In this toy-example, I don't actually need the https://github.com/deusaquilus/anyval_encoder_issue/tree/full_example In reality, object MappedEncoderMaker:
inline def apply[Encoder[_], Mapped <: AnyVal](inline ctx: AnyValEncoderContext[Encoder, Mapped]): Encoder[Mapped] = ${ applyImpl[Encoder, Mapped]('ctx) }
def applyImpl[Encoder[_]: Type, Mapped <: AnyVal: Type](ctx: Expr[AnyValEncoderContext[Encoder, Mapped]])(using qctx: Quotes): Expr[Encoder[Mapped]] =
import qctx.reflect._
val tpe = TypeRepr.of[Mapped]
val firstParam = tpe.typeSymbol.primaryConstructor.paramSymss(0)(0)
val firstParamField = tpe.typeSymbol.memberField(firstParam.name)
val firstParamType = tpe.memberType(firstParamField)
// Try to summon an encoder from the first param type
firstParamType.asType match
case '[tt] =>
Expr.summon[Encoder[tt]] match
case Some(enc) =>
val mappedEncoding = '{ MappedEncoding((v:Mapped) => ${ Select('v.asTerm, firstParamField).asExprOf[tt] }) }
val out = '{ $ctx.makeMappedEncoder[tt]($mappedEncoding, $enc) }
println(s"========== RETURNING Encoder ${tpe.show} => ${firstParamType.show} Consisting of: ${out.show} =========")
out
case None =>
report.throwError(s"Cannot find a regular encoder for the AnyVal type ${tpe.show} or a mapped-encoder for it's base type: ${firstParamType.show}")
... and it is invoked in the implicit inline def anyValEncoder[Cls <: AnyVal]: Encoder[Cls] =
MappedEncoderMaker[Encoder, Cls](
new AnyValEncoderContext[Encoder, Cls] {
override def makeMappedEncoder[Base](mapped: MappedEncoding[Cls, Base], encoder: Encoder[Base]): Encoder[Cls] =
self.mappedEncoder(mapped, encoder)
}
) So I need it to be able to construct the AnyVal instance. One additional note: |
(Some additions in edits in the comment above just now) |
Not sure if it helps but if you make |
I think I found a working workaround. If you make it return a function that takes the EncoderContext and pass the encoder context afterward it works. I.e. if you do this: object MappedEncoderMaker:
inline def apply[T]: EncoderContext => Encoder[T] = ${ applyImpl[T] }
def applyImpl[T: Type](using Quotes): Expr[EncoderContext => Encoder[T]] =
import quotes.reflect._
println(s"===== Creating Instance for: ${Printer.TypeReprShortCode.show(TypeRepr.of[T])}")
'{ (ctx: EncoderContext) => new Encoder[T] { def encode(m: T) = ctx.encode[T](m) } } ... and then call it like this: class EncoderContext { self =>
def encode[Cls](cls: Cls) = List(cls.toString)
implicit inline def anyClsEncoder[Cls]: Encoder[Cls] =
MappedEncoderMaker[Cls].apply(self)
} Hope this info helps. |
Compiler version
RC3
Minimized code
Create a simple class that does something which we want an implicit instance of e.g. an Encoder.
Then create the macro
MappedEncoderMaker
in which we will access this EncoderContext.I decided to also add a print statement to see if the macro is being invoked.
Creating a summoning macro that will attempt to summon the encoder created by our macro:
Finally, create a class that will import the context and attempt to summon the Encoder.
Output
The result is that the summoning macro will not be able to find the Encoder. However, right before, I can see that the MappedEncoderMaker is actually working as intended.
Expectation
Then encoder for the
Wrap
object should be successfully summoned.Note that if you remove the
self
from theMappedEncoderMaker[Cls](self)
call i.e:Then the encoder will be summoned:
Repo
https://github.com/deusaquilus/anyval_encoder_issue
The text was updated successfully, but these errors were encountered: