scala / bug

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

Multiple macro annotations result in no object companion class #12842

Open coreyoconnor opened 10 months ago

coreyoconnor commented 10 months ago

Reproduction steps

Reproduction is in this project: https://gitlab.com/coreyoconnor/multiple-macro-annotation-issue

The reproduction uses sbt run to demo the failure. The failure is not limited to sbt run. I have another project that builds an assembly and sbt-native-packager binary. Both also exhibit the failure. Tho I can't distribute those :)

Problem

Applying one macro annotation that generates or modifies a companion object works fine. Applying two macro annotations and the companion class $.class never output. Which results in a

Caused by: java.lang.ClassNotFoundException: example.C$
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:436)
at sbt.internal.ManagedClassLoader.findClass(ManagedClassLoader.java:103)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 31 more

ClassNotFoundException failure when a method on the companion class is invoked.

The expectation is that the companion class is properly output when both macros are applied.

As far as I can tell using debug-lite the result of the macros is correct: The macro either adds a companion class or adds to the companion class. The resulting expansion shown by debug lite looks correct. However, the C$.class (in this case) is not output at all.

relevant section from the debug lite output:

performing macro expansion new Foo().macroTransform(@new Bar() case class C extends scala.Product with scala.Serializable {
  <caseaccessor> <paramaccessor> val name: String = _;
  def <init>(name: String) = {
    super.<init>();
    ()
  }
}) at RangePosition(/home/coconnor/Development/multiple-macro-annotation-issue/src/main/scala/example/Hello.scala, 109, 109, 112)
{
  @new Bar() case class C extends scala.Product with scala.Serializable {
    <caseaccessor> <paramaccessor> val name: String = _;
    def <init>(name: String) = {
      super.<init>();
      ()
    }
  };
  object C extends Fooer {
    def <init>() = {
      super.<init>();
      ()
    };
    <empty>;
    val doFoo: String = "Foo"
  };
  ()
}
Block(List(ClassDef(Modifiers(CASE, typeNames.EMPTY, List(Apply(Select(New(Ident(TypeName("Bar"))), termNames.CONSTRUCTOR), List()))), TypeName("C"), List(), Template(List(Select(Ident(scala), TypeName("Product")), Select(Ident(scala), TypeName("Serializable"))), noSelfType, List(ValDef(Modifiers(CASEACCESSOR | PARAMACCESSOR), TermName("name"), Ident(TypeName("String")), EmptyTree), DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List(ValDef(Modifiers(PARAM | PARAMACCESSOR), TermName("name"), Ident(TypeName("String")), EmptyTree))), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(()))))))), ModuleDef(Modifiers(), TermName("C"), Template(List(Ident(example.macros.Fooer)), noSelfType, List(DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))), EmptyTree, ValDef(Modifiers(), TermName("doFoo"), Ident(TypeName("String")), Literal(Constant("Foo"))))))), Literal(Constant(())))
performing macro expansion new Bar().macroTransform(case class C extends scala.Product with scala.Serializable {
  <caseaccessor> <paramaccessor> val name: String = _;
  def <init>(name: String) = {
    super.<init>();
    ()
  }
}, object C extends Fooer {
  def <init>() = {
    super.<init>();
    ()
  };
  val doFoo: String = "Foo"
}) at source-/home/coconnor/Development/multiple-macro-annotation-issue/src/main/scala/example/Hello.scala,line-12,offset=114
{
  case class C extends scala.Product with scala.Serializable {
    <caseaccessor> <paramaccessor> val name: String = _;
    def <init>(name: String) = {
      super.<init>();
      ()
    }
  };
  object C extends Fooer with Barer {
    def <init>() = {
      super.<init>();
      ()
    };
    val doFoo: String = "Foo";
    val doBar: String = "Bar"
  };
  ()
}

Note the missing C$.class:

coconnor@pops:~/Development/multiple-macro-annotation-issue$ ls target/scala-2.13/classes/example/
'A$.class'  'B$.class'   C.class  'E$.class'  'F$.class'  'Hello$.class'                   Hello.class
 A.class     B.class     D.class   E.class     F.class    'Hello$delayedInit$body.class'

Credits