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

Type information errors when classloading in macros with Selectable #6494

Open
ncreep opened this issue Jun 10, 2024 · 4 comments
Open

Type information errors when classloading in macros with Selectable #6494

ncreep opened this issue Jun 10, 2024 · 4 comments
Labels
bug Something that is making a piece of functionality unusable Scala 3 Generic ticket relating to Scala 3 upstream-fix-needed Waiting on a fix upstream

Comments

@ncreep
Copy link

ncreep commented Jun 10, 2024

Describe the bug

Hello,

I'm trying to connect to a database via JDBC in a macro, for, um, educational purposes. I then want to use Selectable to reflect the data from the database in a structural type (in the style of type providers).

Despite everything compiling fine, it seems that Metals is having issues when I actually try to touch the database.

Here's a minimized reproduction.

//> using scala "3.4.2"
//> using dep "com.h2database:h2:2.2.224"

import scala.quoted._

class LoadStuff extends Selectable:
  def selectDynamic(name: String): Any = name

object LoadStuff:
  transparent inline def make = ${ makeImpl }
   
  private def makeImpl(using Quotes): Expr[Any] =
    import quotes.reflect.*

    Class.forName("org.h2.Driver")

    val refinement = Refinement(TypeRepr.of[LoadStuff], "name", TypeRepr.of[String])

    refinement.asType match
      case '[refined] => '{ LoadStuff().asInstanceOf[refined] }

Notice that I'm accessing a class from the H2 driver while running the macro. The result of the macro is a refined type.
I can then use it in a separate file as follows:

@main def test =
  val load = LoadStuff.make

  val x = load.name

  println(x)

This compiles and runs correctly. But Metals loses all type information. I can't get member completions on the load value. And I cannot automatically insert a type annotations, it fails with an error.

Following the logs, I see that I get the following error when trying to insert a type annotation:

Jun 11, 2024 12:56:11 AM scala.meta.internal.pc.CompilerAccess handleError
SEVERE: A severe compiler error occurred, full details of the error can be found in the error report /root/workspace/bug-reporting/class_loading_macro/.metals/.reports/metals-full/2024-06-11/r_compiler-error_(class_loading_macro_bd2c96d2de)_00-56-11-868.md
2024.06.11 00:56:11 WARN  Could not find semantic tokens for: file:///.../use_macro.scala

...

Jun 11, 2024 12:56:15 AM org.eclipse.lsp4j.jsonrpc.RemoteEndpoint fallbackResponseError
SEVERE: Internal error: scala.concurrent.Future$$anon$2: Future.filter predicate is not satisfied
java.util.concurrent.CompletionException: scala.concurrent.Future$$anon$2: Future.filter predicate is not satisfied
        at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:332)
        at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:347)
        at java.base/java.util.concurrent.CompletableFuture$UniAccept.tryFire(CompletableFuture.java:708)
        at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
        at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2162)
        at scala.meta.internal.metals.CancelTokens$.$anonfun$future$1(CancelTokens.scala:40)
        at scala.meta.internal.metals.CancelTokens$.$anonfun$future$1$adapted(CancelTokens.scala:38)
        at scala.concurrent.impl.Promise$Transformation.run(Promise.scala:484)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
        at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: scala.concurrent.Future$$anon$2: Future.filter predicate is not satisfied

And opening one of the error files, I see this:

### java.util.NoSuchElementException: head of empty list

...

#### Error stacktrace:

scala.collection.immutable.Nil$.head(List.scala:662)
        scala.collection.immutable.Nil$.head(List.scala:661)
        dotty.tools.pc.PcCollector.<init>(PcCollector.scala:45)
        dotty.tools.pc.PcSemanticTokensProvider$Collector$.<init>(PcSemanticTokensProvider.scala:63)
        dotty.tools.pc.PcSemanticTokensProvider.Collector$lzyINIT1(PcSemanticTokensProvider.scala:63)
        dotty.tools.pc.PcSemanticTokensProvider.Collector(PcSemanticTokensProvider.scala:63)
        dotty.tools.pc.PcSemanticTokensProvider.provide(PcSemanticTokensProvider.scala:88)
        dotty.tools.pc.ScalaPresentationCompiler.semanticTokens$$anonfun$1(ScalaPresentationCompiler.scala:109)

If I remove the Class.forName call everything works as expected, with member completion and generated type annotations.

Please let me know if I can provide any further info.

Thanks

Expected behavior

Selectable types should work correctly, with type member selection hints regardless of the access to library classes.

Operating system

None

Editor/Extension

VS Code

Version of Metals

v1.35.5

Extra context or search terms

No response

@kasiaMarek
Copy link
Contributor

Thanks for the report. It seems that loading java.sql.Driver causes an error in the interactive driver, when inside of a macro.

java.lang.ClassNotFoundException: java.sql.Driver
        at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:421)
        at java.base/java.lang.Class.forName(Class.java:412)
        at a.LoadStuff$.makeImpl(LoadStuff.scala:13)
        at a.LoadStuff$.inline$makeImpl(LoadStuff.scala:10)

@kasiaMarek kasiaMarek added bug Something that is making a piece of functionality unusable upstream-fix-needed Waiting on a fix upstream Scala 3 Generic ticket relating to Scala 3 labels Jun 11, 2024
@ncreep
Copy link
Author

ncreep commented Jun 11, 2024

Thanks for the quick response.

How did you get that stack trace? I didn't see it in the logs that I referenced.

Is the classpath for the interactive driver configurable?

@kasiaMarek
Copy link
Contributor

How did you get that stack trace?

I got it from diagnostics inside of presentation compiler. It isn't surfaced anywhere.

Is the classpath for the interactive driver configurable?

In Metals we take the classpath for the specific build target according to what the build server (so the build tool really) says. But this error only occurs if Class.forName("org.h2.Driver") (or Class.forName("java.sql.Driverr")) in done inside of a macro.

@ncreep
Copy link
Author

ncreep commented Jun 12, 2024

Thanks for the info. If it's the same classpath, I wonder how it diverges from the build tool. Additionally, I can say that this also works correctly when building from sbt (but still broken in Metals).

Actually, thinking about it, this has nothing to do with Selectable. Apparently just being a transparent inline with class loading is enough to trigger this behavior:

object LoadStuff:
  transparent inline def make: Unit = ${ makeImpl }
   
  private def makeImpl(using Quotes): Expr[Unit] =
    Class.forName("org.h2.Driver")

    '{()}

This too triggers a loss of type-information at the call-site of make.

Removing transparent brings back type information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something that is making a piece of functionality unusable Scala 3 Generic ticket relating to Scala 3 upstream-fix-needed Waiting on a fix upstream
Projects
None yet
Development

No branches or pull requests

2 participants