Skip to content

Commit 137aec3

Browse files
jchybtgodzik
authored andcommitted
Do not return java outline dummy constructor in primaryConstructor
Java outline parser phase for various reasons adds a dummy constructor to java classes compiling simultenously from scalac. Since they provide no information to the user and are overall misleading (with always having the same fake flags and parameters), we filter them out and return the first constructor that can be found in the source. This is also what happened up to this point when running the macro with a java classfile on the classpath instead, since those dummy constructors cannot be found there. [Cherry-picked 898f952]
1 parent 16276ec commit 137aec3

File tree

9 files changed

+62
-1
lines changed

9 files changed

+62
-1
lines changed

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2740,7 +2740,26 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27402740
self.typeRef.info.decls.toList
27412741

27422742
def paramSymss: List[List[Symbol]] = self.denot.paramSymss
2743-
def primaryConstructor: Symbol = self.denot.primaryConstructor
2743+
def primaryConstructor: Symbol =
2744+
val initialPrimary = self.denot.primaryConstructor
2745+
// Java outline parser creates a dummyConstructor. We want to avoid returning it here,
2746+
// instead returning the first non-dummy one, which is what happens when a java classfile
2747+
// is read from classpath instead of using the java outline parser.
2748+
// We check if the constructor is dummy if it has the same parameters as defined in JavaParsers.scala,
2749+
// incliding the private[this] flags and parameter shape with scala.Unit argument.
2750+
val isJavaDummyConstructor =
2751+
val paramSymss = initialPrimary.paramSymss
2752+
initialPrimary.flags.is(Flags.JavaDefined | Flags.Local | Flags.Method | Flags.Private | Flags.PrivateLocal)
2753+
&& {
2754+
paramSymss match
2755+
case List(List(typeTree)) if self.typeRef.memberType(typeTree).typeSymbol == defn.UnitClass => true
2756+
case _ => false
2757+
}
2758+
if isJavaDummyConstructor then
2759+
declarations.filter(sym => sym != initialPrimary && sym.isConstructor).headOption.getOrElse(Symbol.noSymbol)
2760+
else
2761+
initialPrimary
2762+
27442763
def allOverriddenSymbols: Iterator[Symbol] = self.denot.allOverriddenSymbols
27452764
def overridingSymbol(ofclazz: Symbol): Symbol =
27462765
if ofclazz.isClass then self.denot.overridingSymbol(ofclazz.asClass)

tests/run-macros/i20052.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
method <init> (Flags.JavaDefined | Flags.Method) List(List((x$0,scala.Int)))
2+
method <init> (Flags.JavaDefined | Flags.Method) List(List())
3+
method <init> (Flags.JavaDefined | Flags.Method | Flags.Private) List(List())
4+
method <init> (Flags.JavaDefined | Flags.Method | Flags.Private) List(List())
5+
method <init> (Flags.JavaDefined | Flags.Method) List(List((A,_ >: scala.Nothing <: <special-ops>.<FromJavaObject>)), List((x$0,A)))
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public class JavaClass {
2+
public JavaClass(int a) {}
3+
public JavaClass(float a) {}
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public class JavaClassEmpty {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public class JavaClassParam<A> {
2+
public JavaClassParam(A a) {}
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class JavaClassPrivate {
2+
private JavaClassPrivate() {}
3+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class JavaClassStartsWithPrivate {
2+
private JavaClassStartsWithPrivate() {}
3+
public JavaClassStartsWithPrivate(int a) {}
4+
}

tests/run-macros/i20052/Macro.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import scala.quoted.*
2+
3+
object Macro {
4+
inline def logPrimaryConstructor[A]: String = ${ logPrimaryConstructorImpl[A] }
5+
6+
def logPrimaryConstructorImpl[A](using Type[A], Quotes): Expr[String] = {
7+
import quotes.reflect.*
8+
9+
val primaryConstructor = TypeRepr.of[A].typeSymbol.primaryConstructor
10+
val flags = primaryConstructor.flags.show
11+
val paramSymss = primaryConstructor.paramSymss
12+
val clauses = paramSymss.map(_.map(param => (param.name, TypeRepr.of[A].memberType(param).show)))
13+
val str = s"${primaryConstructor} (${primaryConstructor.flags.show}) ${clauses}"
14+
Expr(str)
15+
}
16+
}

tests/run-macros/i20052/Test_2.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@main def Test() =
2+
println(Macro.logPrimaryConstructor[JavaClass])
3+
println(Macro.logPrimaryConstructor[JavaClassEmpty])
4+
println(Macro.logPrimaryConstructor[JavaClassPrivate])
5+
println(Macro.logPrimaryConstructor[JavaClassStartsWithPrivate])
6+
println(Macro.logPrimaryConstructor[JavaClassParam[Int]])

0 commit comments

Comments
 (0)