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

Making case class constructor package-private crashes the compiler when there's a circular dependency #19526

Closed
lbialy opened this issue Jan 24, 2024 · 2 comments · Fixed by #19688
Assignees
Labels
area:inline area:metaprogramming:quotes Issues related to quotes and splices itype:bug itype:crash regression This worked in a previous version but doesn't anymore
Milestone

Comments

@lbialy
Copy link

lbialy commented Jan 24, 2024

Compiler version

3.3.1

Minimized code

This caused by a circular reference between stack.scala (the trait StackFactory references Export) and macro.scala. If the trait is moved to another file this example compiles (and that's the workaround).

3 files, same directory, scala-cli setup:

package.scala

//> using scala 3.3.1

stack.scala

package crash.test

case class Stack private[crash] ( // remove private[crash] to make error go away
    exports: String,
    dependsOn: Vector[Int]
)

trait StackFactory:
  val exports: Export.type = Export

  def apply(dependsOn: Int*): Stack =
    Export().copy(dependsOn = dependsOn.toVector)

macro.scala

package crash.test

import scala.language.dynamics

import scala.quoted.*

object Export extends Dynamic:
  inline def applyDynamic(name: "apply")(inline args: Any*): Stack = ${
    applyDynamicImpl('args)
  }

  def applyDynamicImpl(args: Expr[Seq[Any]])(using Quotes): Expr[Stack] =
    import quotes.reflect.*

    '{ Stack("", Vector.empty) }

Output (click arrow to expand)

scala-cli compile --server=false .

  unhandled exception while running inlining on /Users/lbialy/Projects/foss/dotty-crash-replication/stack.scala

  An unhandled exception was thrown in the compiler.
  Please file a crash report here:
  https://github.com/lampepfl/dotty/issues/new/choose

     while compiling: <no file>
        during phase: <no phase>
                mode: Mode(ImplicitsEnabled)
     library version: version 2.13.10
    compiler version: version 3.3.1
            settings: -classpath /Users/lbialy/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.3.1/scala3-library_3-3.3.1.jar:/Users/lbialy/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.10/scala-library-2.13.10.jar -d /Users/lbialy/Projects/foss/dotty-crash-replication/.scala-build/dotty-crash-replication_3a37a7372a/classes/main -java-output-version 17 -sourceroot /Users/lbialy/Projects/foss/dotty-crash-replication

                tree: EmptyTree
       tree position: :<unknown>
           tree type: <notype>
              symbol: val <none>
           call site: package <root> in module class <root>

  == Source file context for tree position ==


Exception while compiling /Users/lbialy/Projects/foss/dotty-crash-replication/macro.scala, /Users/lbialy/Projects/foss/dotty-crash-replication/package.scala, /Users/lbialy/Projects/foss/dotty-crash-replication/stack.scala
Exception in thread "main" java.lang.NoClassDefFoundError: crash/test/Stack$
	at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
	at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3402)
	at java.base/java.lang.Class.getMethodsRecursive(Class.java:3543)
	at java.base/java.lang.Class.getMethod0(Class.java:3529)
	at java.base/java.lang.Class.getMethod(Class.java:2225)
	at dotty.tools.dotc.quoted.Interpreter.getMethod(Interpreter.scala:219)
	at dotty.tools.dotc.quoted.Interpreter.interpretedStaticMethodCall(Interpreter.scala:171)
	at dotty.tools.dotc.quoted.Interpreter.interpretTree(Interpreter.scala:80)
	at dotty.tools.dotc.transform.Splicer$SpliceInterpreter.interpretTree(Splicer.scala:258)
	at dotty.tools.dotc.quoted.Interpreter.interpretTree$$anonfun$2(Interpreter.scala:99)
	at dotty.tools.dotc.transform.Splicer$.$anonfun$2(Splicer.scala:60)
	at scala.Option.fold(Option.scala:263)
	at dotty.tools.dotc.transform.Splicer$.splice(Splicer.scala:60)
	at dotty.tools.dotc.inlines.Inliner.dotty$tools$dotc$inlines$Inliner$$expandMacro(Inliner.scala:1048)
	at dotty.tools.dotc.inlines.Inliner$InlineTyper.typedSplice(Inliner.scala:839)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3092)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3112)
	at dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:146)
	at dotty.tools.dotc.inlines.Inliner$InlineTyper.typedUnadapted(Inliner.scala:915)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3184)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3188)
	at dotty.tools.dotc.typer.ReTyper.typedTyped(ReTyper.scala:65)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3053)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3112)
	at dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:146)
	at dotty.tools.dotc.inlines.Inliner$InlineTyper.typedUnadapted(Inliner.scala:915)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3184)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3181)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3188)
	at dotty.tools.dotc.inlines.Inliner.inlined(Inliner.scala:681)
	at dotty.tools.dotc.inlines.Inlines$InlineCall.expand(Inlines.scala:444)
	at dotty.tools.dotc.inlines.Inlines$.inlineCall(Inlines.scala:152)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:95)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1567)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:67)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:89)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.loop$2(tpd.scala:1244)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1244)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformBlock(tpd.scala:1249)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1518)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:43)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:91)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:50)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:89)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.loop$2(tpd.scala:1244)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1244)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1246)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:58)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:108)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1572)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:67)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:89)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.loop$2(tpd.scala:1244)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1244)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1246)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1580)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:67)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:97)
	at dotty.tools.dotc.transform.Inlining$$anon$2.transform(Inlining.scala:58)
	at dotty.tools.dotc.transform.MacroTransform.run(MacroTransform.scala:18)
	at dotty.tools.dotc.transform.Inlining.run(Inlining.scala:34)
	at dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:327)
	at scala.collection.immutable.List.map(List.scala:246)
	at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:331)
	at dotty.tools.dotc.transform.Inlining.runOn(Inlining.scala:38)
	at dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:246)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1321)
	at dotty.tools.dotc.Run.runPhases$1(Run.scala:262)
	at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:270)
	at dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:279)
	at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:67)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:279)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:200)
	at dotty.tools.dotc.Driver.finish(Driver.scala:58)
	at dotty.tools.dotc.Driver.doCompile(Driver.scala:38)
	at dotty.tools.dotc.Driver.process(Driver.scala:197)
	at dotty.tools.dotc.Driver.process(Driver.scala:165)
	at dotty.tools.dotc.Driver.process(Driver.scala:177)
	at dotty.tools.dotc.Driver.main(Driver.scala:207)
	at dotty.tools.dotc.Main.main(Main.scala)
Caused by: java.lang.ClassNotFoundException: crash.test.Stack$
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:587)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	... 82 more
Compilation failed
@lbialy lbialy added itype:bug itype:crash stat:needs triage Every issue needs to have an "area" and "itype" label labels Jan 24, 2024
@WojciechMazur
Copy link
Contributor

It seems to be a regression introduced in 3.0.3-RC1-bin-20210804-07c3b4d-NIGHTLY, bisect points to 9fd494a

@WojciechMazur WojciechMazur added area:inline regression This worked in a previous version but doesn't anymore area:metaprogramming:quotes Issues related to quotes and splices and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jan 24, 2024
@nicolasstucki
Copy link
Contributor

The crash is already fixed by #19645.

The code now has a compilation error due to a cyclic dependency between the two files. This dependency is a consequence of the unstable inline accessor that gets generated by the access to private the constructor of Stack. This can be detected with -WunstableInlineAccessors.

There are two ways to fix this code:

  1. Split Stack and StackFactory into two files. Stack and export can be compiled before it is used in StackFactory
  2. Avoid the creation of the inline acceesors by defining the apply method of Stack with @publicInBinary
object Stack:
  @scala.annotation.publicInBinary
  private[crash] def apply(exports: String, dependsOn: Vector[Int]): Stack = new Stack(exports, dependsOn)

nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Feb 14, 2024
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Feb 14, 2024
@Kordyjan Kordyjan added this to the 3.4.2 milestone Mar 28, 2024
@Kordyjan Kordyjan modified the milestones: 3.4.2, 3.5.0 May 10, 2024
WojciechMazur pushed a commit that referenced this issue Jul 1, 2024
Closes #19526

[Cherry-picked 2c32436]
WojciechMazur pushed a commit that referenced this issue Jul 2, 2024
Closes #19526

[Cherry-picked 2c32436]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:inline area:metaprogramming:quotes Issues related to quotes and splices itype:bug itype:crash regression This worked in a previous version but doesn't anymore
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants