Skip to content
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

RequireMsg compile-time error message escaping code context #234

Open
tribbloid opened this issue Mar 14, 2022 · 6 comments
Open

RequireMsg compile-time error message escaping code context #234

tribbloid opened this issue Mar 14, 2022 · 6 comments

Comments

@tribbloid
Copy link

This is a short code snippet that demonstrates the problem:

import singleton.ops.RequireMsg

object RequireMsgSpike {

  trait HasM {

    type M = Int
  }

  trait Foo {

    trait FF[T]
  }

  object Foo3 extends Foo {

    implicit def ev3[T <: HasM](
        implicit
        r: RequireMsg[false, "Just Bad Type"]
    ): Set[FF[T]] = ???
  }

  object Foo4 extends Foo {

    implicit def ev4[T <: HasM](
        implicit
        r: Int
    ): Set[FF[T]] = ???
  }

  implicitly[Set[Foo3.FF[HasM]]]
  implicitly[Set[Foo4.FF[HasM]]]
}

error message:

[Error] /***/RequireMsgSpike.scala:34:13: Just Bad Type
[Error] /***/RequireMsgSpike.scala:35:13: Just Bad Type

The second implicit error message got corrupted by the first.

if you delete the first implicit line implicitly[Set[Foo3.FF[HasM]]], the error message goes back to normal:

[Error] /home/peng/git/shapesafe/core/src/test/scala/shapesafe/core/debugging/RequireMsgSpike.scala:35:13: could not find implicit value for parameter e: Set[shapesafe.core.debugging.RequireMsgSpike.Foo4.FF[shapesafe.core.debugging.RequireMsgSpike.HasM]] (No implicit view available from shapesafe.core.debugging.RequireMsgSpike.Foo4.FF[shapesafe.core.debugging.RequireMsgSpike.HasM] => Boolean.)

I vaguely remember that RequireMsg macro overwrites @ImplicitNotFound annotation to achieve its purpose, and this implementation is derived from shapeless. So I guess the easiest way for me is to try the following solution:

  • try to reproduce it in shapeless
    • if succeed, forward-port it to singleton-ops
    • otherwise, file the same issue for shapeless, and try to write an original fix or circumvention
@soronpo
Copy link
Collaborator

soronpo commented Mar 15, 2022

This could be due to the caching mechanism in singleton-ops.

@tribbloid
Copy link
Author

you are most likely right, there is no difference in shapeless code.

In the meantime I'll try to PR a piece of unit test first.

@soronpo
Copy link
Collaborator

soronpo commented Mar 15, 2022

Actually this kind of looks like a Scalac bug, but maybe as a result of that tampering with @implicitNotFound via macros.

@tribbloid
Copy link
Author

@soronpo It turns out that the error is only triggered in certain cases. Please see the new test case in my PR

@tribbloid
Copy link
Author

This could be due to the caching mechanism in singleton-ops.

I'm now 90% sure it wasn't. By observing what is being annotated by your code:

After logging the abort function:

  def abort(msg: String, annotatedSym : Option[TypeSymbol] = defaultAnnotatedSym): Nothing = {
    VerboseTraversal(s"!!!!!!aborted with: $msg at $annotatedSym, $defaultAnnotatedSym")

    annotatedSym.foreach {
      sym =>

        val oldAnnotation = sym.annotations

        setAnnotation(msg, sym)

        println(s"setting annotation of `${annotatedSym.get}`: `${oldAnnotation.mkString(",")}` --> `${msg}`")
    }
...

I observe the following message in compile-time:

setting annotation of `type Set`: `` --> `Testing 456`
...
[error] /home/peng/git-release/singleton-ops/src/test/scala/singleton/ops/RequireSpec.scala:43:13: Type-checking failed in an unexpected way.
[error] Expected error matching: could not find implicit value.*
[error] Actual error: Testing 456
[error]     illTyped("""implicitly[Set[Int]]""","could not find implicit value.*")
[error]             

Clearly, the annotation of "Set" was changed, which even without caching, affects all other implicits with "Set[*]" return type.

To solve this, you'll have 3 options:

  1. revert the annotation immediately after aborting the context
  2. apply the annotation on the function (should be a scala 2.13 feature), instead of the return type
  3. leverage the following property of blackbox macro to fail with an error message immediately, instead of delegating to implicit search:

If a blackbox macro (even implicit blackbox macro) throws an exception then it will be a compile error during compilation of main code. If a whitebox implicit macro throws an exception then during compilation of main code the implicit will be silently removed from candidates.

(Source: https://stackoverflow.com/questions/64538997/what-should-a-scala-implicit-macro-have-to-return-to-tell-the-compiler-forget-m)

Not sure if this information is helpful

@tribbloid
Copy link
Author

... Sorry the last option is out of context & doesn't apply to your case. The implicit error will always write over the previous error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants