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

Can't get constructor of java defined class in reflect api #18694

Open
goshacodes opened this issue Oct 13, 2023 · 5 comments
Open

Can't get constructor of java defined class in reflect api #18694

goshacodes opened this issue Oct 13, 2023 · 5 comments
Assignees
Labels
area:metaprogramming:reflection Issues related to the quotes reflection API itype:bug itype:question

Comments

@goshacodes
Copy link

goshacodes commented Oct 13, 2023

Compiler version

3.3.0

Description

I'm trying to extend a java class using reflect api.
To create a symbol for a class I need parent, either TypeTree or Term. TypeTree is not working here, so I'am trying to get a constructor and apply nulls as arguments

Minimized code

public class JavaClassWithBridgeMethod {
    public String overloadedMethod(String a) { return "bar1"; }
}
def macro[T: Type](using quotes: Quotes): Expr[Any] =
  import quotes.reflect.*
  val tpe = TypeRepr.of[T]
  ...

macro[JavaClassWithBridgeMethod]

When I'm trying to use tpe.typeSymbol.primaryConstructor compiler crushes with this:

[error] java.lang.AssertionError: assertion failed: private constructor JavaClassWithBridgeMethod in class JavaClassWithBridgeMethod in /Users/g.kovalev/IdeaProjects/ScalaMock/jvm/src/test/java/com/paulbutcher/test/JavaClassWithBridgeMethod.java accessed from constructor $anon in /Users/g.kovalev/IdeaProjects/ScalaMock/jvm/src/test/scala/com.paulbutcher.test/mock/JavaMocksTest.scala
[error] scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
[error] dotty.tools.dotc.transform.ExpandPrivate.ensurePrivateAccessible(ExpandPrivate.scala:88)
[error] dotty.tools.dotc.transform.ExpandPrivate.transformSelect(ExpandPrivate.scala:98)
[error] dotty.tools.dotc.transform.ExpandPrivate.transformSelect(ExpandPrivate.scala:97)
[error] dotty.tools.dotc.transform.MegaPhase.goSelect(MegaPhase.scala:605)
[error] dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:228)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:425)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:278)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
[error] dotty.tools.dotc.transform.MegaPhase.loop$2(MegaPhase.scala:444)
[error] dotty.tools.dotc.transform.MegaPhase.transformBlock(MegaPhase.scala:449)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:298)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
[error] dotty.tools.dotc.transform.MegaPhase.mapDefDef$1(MegaPhase.scala:248)
[error] dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:251)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:425)
[error] dotty.tools.dotc.transform.MegaPhase.transformSpecificTree(MegaPhase.scala:434)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:356)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
[error] dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:255)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:425)
[error] dotty.tools.dotc.transform.MegaPhase.loop$1(MegaPhase.scala:438)
[error] dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:438)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:359)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
[error] dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:255)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:425)
[error] dotty.tools.dotc.transform.MegaPhase.loop$1(MegaPhase.scala:438)
[error] dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:438)
[error] dotty.tools.dotc.transform.MegaPhase.mapPackage$1(MegaPhase.scala:379)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:382)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnit(MegaPhase.scala:454)
[error] dotty.tools.dotc.transform.MegaPhase.run(MegaPhase.scala:466)
[error] dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:324)
[error] scala.collection.immutable.List.map(List.scala:250)
[error] dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:328)
[error] dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:247)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
[error] scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1321)
[error] dotty.tools.dotc.Run.runPhases$1(Run.scala:263)
[error] dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:271)
[error] dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:280)
[error] dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:67)
[error] dotty.tools.dotc.Run.compileUnits(Run.scala:280)
[error] dotty.tools.dotc.Run.compileSources(Run.scala:195)
[error] dotty.tools.dotc.Run.compile(Run.scala:179)
[error] dotty.tools.dotc.Driver.doCompile(Driver.scala:35)
[error] dotty.tools.xsbt.CompilerBridgeDriver.run(CompilerBridgeDriver.java:88)
[error] dotty.tools.xsbt.CompilerBridge.run(CompilerBridge.java:22)
[error] sbt.internal.inc.AnalyzingCompiler.compile(AnalyzingCompiler.scala:91)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$7(MixedAnalyzingCompiler.scala:193)
[error] scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
[error] sbt.internal.inc.MixedAnalyzingCompiler.timed(MixedAnalyzingCompiler.scala:248)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$4(MixedAnalyzingCompiler.scala:183)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$4$adapted(MixedAnalyzingCompiler.scala:163)
[error] sbt.internal.inc.JarUtils$.withPreviousJar(JarUtils.scala:239)
[error] sbt.internal.inc.MixedAnalyzingCompiler.compileScala$1(MixedAnalyzingCompiler.scala:163)
[error] sbt.internal.inc.MixedAnalyzingCompiler.compile(MixedAnalyzingCompiler.scala:211)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1$adapted(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.Incremental$.$anonfun$apply$5(Incremental.scala:179)
[error] sbt.internal.inc.Incremental$.$anonfun$apply$5$adapted(Incremental.scala:177)
[error] sbt.internal.inc.Incremental$$anon$2.run(Incremental.scala:463)
[error] sbt.internal.inc.IncrementalCommon$CycleState.next(IncrementalCommon.scala:116)
[error] sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:56)
[error] sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:52)
[error] sbt.internal.inc.IncrementalCommon.cycle(IncrementalCommon.scala:263)
[error] sbt.internal.inc.Incremental$.$anonfun$incrementalCompile$8(Incremental.scala:418)
[error] sbt.internal.inc.Incremental$.withClassfileManager(Incremental.scala:505)
[error] sbt.internal.inc.Incremental$.incrementalCompile(Incremental.scala:405)
[error] sbt.internal.inc.Incremental$.apply(Incremental.scala:171)
[error] sbt.internal.inc.IncrementalCompilerImpl.compileInternal(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileIncrementally$1(IncrementalCompilerImpl.scala:488)
[error] sbt.internal.inc.IncrementalCompilerImpl.handleCompilationError(IncrementalCompilerImpl.scala:332)
[error] sbt.internal.inc.IncrementalCompilerImpl.compileIncrementally(IncrementalCompilerImpl.scala:425)
[error] sbt.internal.inc.IncrementalCompilerImpl.compile(IncrementalCompilerImpl.scala:137)
[error] sbt.Defaults$.compileIncrementalTaskImpl(Defaults.scala:2363)
[error] sbt.Defaults$.$anonfun$compileIncrementalTask$2(Defaults.scala:2313)
[error] sbt.internal.server.BspCompileTask$.$anonfun$compute$1(BspCompileTask.scala:30)
[error] sbt.internal.io.Retry$.apply(Retry.scala:46)
[error] sbt.internal.io.Retry$.apply(Retry.scala:28)
[error] sbt.internal.io.Retry$.apply(Retry.scala:23)
[error] sbt.internal.server.BspCompileTask$.compute(BspCompileTask.scala:30)
[error] sbt.Defaults$.$anonfun$compileIncrementalTask$1(Defaults.scala:2311)
[error] scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error] sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
[error] sbt.std.Transform$$anon$4.work(Transform.scala:68)
[error] sbt.Execute.$anonfun$submit$2(Execute.scala:282)
[error] sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
[error] sbt.Execute.work(Execute.scala:291)
[error] sbt.Execute.$anonfun$submit$1(Execute.scala:282)
[error] sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error] sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
[error] java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
[error] java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:577)
[error] java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
[error] java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
[error] java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)

So instead of trying to use primary constructor, I am trying to use another public one and I got this one.

wrong number of arguments at dropBreaks for (x$1: Unit): com.paulbutcher.test.JavaClassWithBridgeMethod: (com.paulbutcher.test.JavaClassWithBridgeMethod#<init> :
[error]    |  (x$1: Unit): com.paulbutcher.test.JavaClassWithBridgeMethod), expected: 1, found: 0

After it I am trying to pass there a () as argument and it gives me another error:

wrong number of arguments at pickler for (): com.paulbutcher.test.JavaClassWithBridgeMethod: (com.paulbutcher.test.JavaClassWithBridgeMethod#<init> :
[error]    |  (): com.paulbutcher.test.JavaClassWithBridgeMethod), expected: 0, found: 1
scala.MatchError: val <none> (of class dotty.tools.dotc.core.Symbols$NoSymbol$)
@goshacodes goshacodes added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Oct 13, 2023
@nicolasstucki nicolasstucki added itype:question area:metaprogramming:reflection Issues related to the quotes reflection API itype:bug and removed stat:needs triage Every issue needs to have an "area" and "itype" label itype:bug labels Oct 16, 2023
@nicolasstucki
Copy link
Contributor

@goshacodes could you provide the implementation of the macro?

@goshacodes
Copy link
Author

goshacodes commented Oct 16, 2023

@goshacodes
Copy link
Author

goshacodes commented Jan 7, 2025

@nicolasstucki minimized example. Also I can try contribute this with a spree, want this fixed

build.sbt

lazy val root = (project in file("."))
  .settings(
    scalaVersion := "3.6.2",
    name := "java_class",
    scalacOptions ++= Seq(
      "-explain",
      "-Xcheck-macros"
    )
  )

JavaClass.java

public class JavaClass {
}

main.scala

import scala.annotation.experimental

@main
@experimental
def main(): Unit =
  mock[JavaClass]

Macro.scala

import scala.annotation.experimental
import scala.quoted.{Expr, Quotes, Type}

@experimental
inline def mock[T]: T = ${mockMacro[T]}

@experimental
def mockMacro[T: Type](using quotes: Quotes): Expr[T] =
  import quotes.reflect.*
  val tree = TypeTree.of[T]
  val parents =
    List(
      Select(
        New(TypeIdent(tree.tpe.typeSymbol)),
        tree.tpe.typeSymbol.primaryConstructor
      ).appliedToArgs(List('{()}.asTerm))
    )

  val sym = Symbol.newClass(
    Symbol.spliceOwner,
    "anon",
    parents.map {
      case term: Term => term.tpe
      case tpt: TypeTree => tpt.tpe
    },
    decls = _ => Nil,
    selfType = None
  )

  val cls = ClassDef(sym, parents, Nil)

  Block(
    List(cls),
    Typed(Apply(Select(New(TypeIdent(sym)), sym.primaryConstructor), Nil), TypeTree.of[T])
  ).asExprOf[T]

Output

[error] java.lang.AssertionError: assertion failed: private constructor JavaClass in class JavaClass in /Users/gosha/IdeaProjects/java_class/src/main/java/JavaClass.java accessed from constructor anon in class anon in /Users/gosha/IdeaProjects/java_class/src/main/scala/main.scala
[error] scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
[error] dotty.tools.dotc.transform.ExpandPrivate.ensurePrivateAccessible(ExpandPrivate.scala:88)
[error] dotty.tools.dotc.transform.ExpandPrivate.transformSelect(ExpandPrivate.scala:98)
[error] dotty.tools.dotc.transform.ExpandPrivate.transformSelect(ExpandPrivate.scala:97)
[error] dotty.tools.dotc.transform.MegaPhase.goSelect(MegaPhase.scala:636)
[error] dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:245)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:452)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:295)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:454)
[error] dotty.tools.dotc.transform.MegaPhase.loop$2(MegaPhase.scala:471)

@goshacodes
Copy link
Author

@jchyb Also tagging you. Nicolas seems not active on github currently

@jchyb jchyb self-assigned this Jan 7, 2025
@jchyb
Copy link
Contributor

jchyb commented Jan 7, 2025

Yes, I recently took over some of Nicolas' responsibilities. Thank you for the minimization! I believe this might be getting fixed soon with #22104. I'll make sure it is in 3.6.4 (should be back portable to LTS).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:metaprogramming:reflection Issues related to the quotes reflection API itype:bug itype:question
Projects
None yet
Development

No branches or pull requests

3 participants