scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
230 stars 21 forks source link

Quill idiom for inferred implicit type with whitebox macro under `-Xsource:3` #12798

Closed jilen closed 12 months ago

jilen commented 1 year ago

The scala 2.13.11 version requires explicit type of implicit definition. Then it become impossible to use whitebox macro to return the narrowed type.

SethTisue commented 1 year ago

The change in question is scala/scala#10083. /cc @som-snytt

Can you give an example of what you can't do now?

SethTisue commented 1 year ago

Can the macro emit @annotation.nowarn("cat=other-implicit-type")?

som-snytt commented 1 year ago

This is pre-second-coffee, but you can always write : Any and the whitebox macro produces a narrower type.

SethTisue commented 1 year ago

@som-snytt any advice we have for macro authors would be good to add to the PR description

som-snytt commented 1 year ago

I added a line to the PR without thinking about it, but please add a use case here if necessary.

jilen commented 1 year ago

@SethTisue

Can you give an example of what you can't do now?

The case for me is defining quill's QueryMeta

implicit val  personMeta = materializedQueryMeta[Person]

Quill will use the concrete type info later. If specify type to QueryMeta[Person] it will become dynamic query.

som-snytt commented 1 year ago

@jilen Does that mean implicit val personMeta: Any = materializedQueryMeta[Person] does not work? I looked briefly at quill last year in the context of a bug related to macro and result inference, but I don't remember much about it.

jilen commented 1 year ago

@som-snytt Confirmed adding : Any gets things done. Thanks

jilen commented 1 year ago

My bad, just find Any not working

//> using lib org.scala-lang:scala-reflect:2.13.11
//> using scala "2.13.11"
//> using options -Xsource:3

import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros

class FooMacroImpl(val c: Context) {
  import c.universe._

  def fooImpl: Tree = {
    val c = Constant("foo")
    q"${c}"
  }
}

object Foo {
  def foo: String  = macro FooMacroImpl.fooImpl
}

Then

scala> implicit val foo: Any = Foo.foo
val foo: Any = foo
SethTisue commented 1 year ago

@jilen what behavior did you expect, there?

jilen commented 1 year ago

@jilen what behavior did you expect, there?

implicit val foo: Any = Foo.foo

I thought the type of foo should be String (returned by whitebox macro)

som-snytt commented 1 year ago

I'll take a look today. I don't want anything to spoil Seth's Scala Days. Because really, how many Scala Days do have in this life? Every Scala Day is precious.

SethTisue commented 1 year ago

I thought the type of foo should be String (returned by whitebox macro)

Ah, I had misunderstood Som's suggestion, but I understand better now.

I'm surprised to learn that that would be expected behavior. when I write implicit val foo: Any = ..., I would not have expected anything I could substitute for ... to have any influence on what type foo gets, give that I explicitly asked for the type to be Any.

@som-snytt you're sure that's expected...?

I guess in Scala 3 this issue can't arise because there aren't whitebox macros...? Perhaps one possible resolution here would be to say that it's expected for -Xsource:3 to complain when you're doing something that isn't even possible in Scala 3 at all?

perhaps @guizmaii could enlighten us on how people using Scala 3 with Quill would be expected to write this code?

som-snytt commented 1 year ago

@SethTisue Yes, it's fundamental to whiteboxness and also works that way in Scala 3 via transparent inline.

Oh wait, sorry lack of sleep, the implicit is in the wrong place.

implicit def myimplicit: Any = macro mywhitebox

lets the type of myimplicit expansion get refined.

I'll take a look at how the behavior is relied upon in quill.

SethTisue commented 1 year ago

implicit def myimplicit: Any = macro mywhitebox lets the type of myimplicit expansion get refined.

Okay, that makes more sense.

But it doesn't seem to help OP. Their desire to have an implicit val with an inferred type is fundamentally at odds with the explicit-type requirement.

also works that way in Scala 3 via transparent inline

I'd like to see a spelled-out example of that; in Scala 3, can I have a given whose type isn't written, but comes from a transparent inline?

SethTisue commented 1 year ago

@som-snytt One thing I don't know here is whether the @nowarn mechanism is sufficiently powerful to convert an error (at least some kind of errors, I mean) into a warning, or to silence it altogether? Or does the way scala/scala#10083 is implemented prevent that?

som-snytt commented 1 year ago

They call it issueNormalTypeError. So it means no soup for you. Now we also know why it's called quill, it's not the writing implement but the porcupine defense mechanism. I would have named it sharpie to make the dual metaphor explicit.

dwijnand commented 1 year ago

I'd like to see a spelled-out example of that; in Scala 3, can I have a given whose type isn't written, but comes from a transparent inline?

It's written but it's also transparent: https://dotty.epfl.ch/docs/reference/contextual/givens.html#given-macros-1

leszekgruchala commented 1 year ago

We cannot update to 2.13.11 because of this issue and the fact we cannot add explicit types for Quill. We use -Xsource:3 and I see -Wconf:cat=other-implicit-type:s is simply being ignored with that option together.

lrytz commented 1 year ago

Not sure if this is clear from the discussion, sorry if I'm just adding noise; the static type of an implicit whitebox macro def defines what type can be summoned:

import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros
class A
class B extends A

class FooMacroImpl(val c: Context) {
  import c.universe._
  def fooImpl: Tree = {
    q"new B()"
  }
}
object Foo {
  implicit def foo: A = macro FooMacroImpl.fooImpl
}
scala> import Foo._
import Foo._

scala> foo
val res0: B = B@49005dc4

scala> implicitly[B]
                 ^
       error: could not find implicit value for parameter e: B

scala> implicitly[A]
val res2: A = B@a1e578f
lrytz commented 1 year ago

A good approach to migration errors under -Xsource:3 could be to make them fatal warnings instead, and allow users to override that default using -Wconf / @nowarn.

som-snytt commented 1 year ago

I'll pursue that approach: instead of knobs to enable migration behaviors, -Wconf provides levers for the warnings, and for behaviors such as override inference, emit warnings. The implicit search change was split out, and never received subsequent attention. The are a handful of behaviors which are not mere syntax or checks.

Note to self: one way to simplify -Xlint/-Wconf is instead of enabling lints, you silence their output. (Internally, silencing a lint category could just turn off the lint.)

nafg commented 9 months ago

Bottom line what do I do with scala 2.13.12?

som-snytt commented 9 months ago

Top line, the idiom of the title is -Wconf:cat=scala3-migration:s .

Sample potential dogfooding at https://github.com/scala/scala/pull/10551/files#diff-5634c415cd8c8504fdb973a3ed092300b43c4b8fc1e184f7249eb29a55511f91R417

compile / scalacOptions ++= Seq("-Wconf:cat=scala3-migration&msg=elidable&site=scala.Predef:s"),

The wconf is :e by default to error, so :s silences it and :w dials it back to warning, etc.