Skip to content

Macro annotation that processes inlined tree crashes under -Ycheck:all #17007

Open
@pweisenburger

Description

@pweisenburger

Compiler version

3.3.1-RC1-bin-20230216-2507577-NIGHTLY

Minimized code

Consider the following macro definition (the macro annotation extracts the number v that is applied to locally in inlineMethod):

import scala.annotation.MacroAnnotation
import scala.quoted.*

class annotation extends MacroAnnotation:
  def transform(using Quotes)(tree: quotes.reflect.Definition) =
    import quotes.reflect.*

    tree match
      case tree: ClassDef =>
        val List(DefDef(name, paramss, tpt, Some(body))) = tree.body: @unchecked

        val rhs = body match
          case Inlined(_, _, Block(List(Apply(_ /* `locally` */, List(rhs))), _)) => rhs

        val method = DefDef.copy(tree.body.head)(name, paramss, tpt, Some(rhs.changeOwner(tree.body.head.symbol)))

        List(ClassDef.copy(tree)(tree.name, tree.constructor, tree.parents, tree.self, List(method)))

Macro call site:

transparent inline def inlineMethod(v: Int) =
  locally(v)
  0

@annotation
class Test:
  def method = inlineMethod(42)

Output

When compiling with -Ycheck:all, the compiler crashes; otherwise, the code compiles. It seems that the transparent inline method is crucial to produce the crash.

Error and Stack Trace
*** error while checking Test.scala after phase inlining ***
java.util.NoSuchElementException: head of empty list while running Ycheck on Test.scala
java.util.NoSuchElementException: head of empty list while compiling Test.scala, TestMacro.scala
[error] ## Exception when compiling 2 sources
[error] java.util.NoSuchElementException: head of empty list
[error] scala.collection.immutable.Nil$.head(List.scala:662)
[error] scala.collection.immutable.Nil$.head(List.scala:661)
[error] dotty.tools.dotc.transform.YCheckPositions$$anon$1.traverse(YCheckPositions.scala:45)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.apply(Trees.scala:1660)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.apply(Trees.scala:1660)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeAccumulator.foldOver(Trees.scala:1620)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.traverseChildren(Trees.scala:1661)
[error] dotty.tools.dotc.transform.YCheckPositions$$anon$1.traverse(YCheckPositions.scala:53)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.apply(Trees.scala:1660)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.apply(Trees.scala:1660)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeAccumulator.fold$1(Trees.scala:1532)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeAccumulator.apply(Trees.scala:1534)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeAccumulator.foldOver(Trees.scala:1627)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.traverseChildren(Trees.scala:1661)
[error] dotty.tools.dotc.transform.YCheckPositions$$anon$1.traverse(YCheckPositions.scala:53)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.apply(Trees.scala:1660)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.apply(Trees.scala:1660)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeAccumulator.foldOver(Trees.scala:1624)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.traverseChildren(Trees.scala:1661)
[error] dotty.tools.dotc.transform.YCheckPositions$$anon$1.traverse(YCheckPositions.scala:53)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.apply(Trees.scala:1660)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.apply(Trees.scala:1660)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeAccumulator.fold$1(Trees.scala:1532)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeAccumulator.apply(Trees.scala:1534)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeAccumulator.foldOver(Trees.scala:1633)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeTraverser.traverseChildren(Trees.scala:1661)
[error] dotty.tools.dotc.transform.YCheckPositions$$anon$1.traverse(YCheckPositions.scala:53)
[error] dotty.tools.dotc.transform.YCheckPositions.checkPostCondition(YCheckPositions.scala:58)
[error] dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted$$anonfun$1(TreeChecker.scala:412)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
[error] scala.collection.immutable.List.foreach(List.scala:333)
[error] dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:412)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3077)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:3081)
[error] dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:378)
[error] dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3193)
[error] dotty.tools.dotc.transform.TreeChecker.check(TreeChecker.scala:126)
[error] dotty.tools.dotc.transform.TreeChecker.run(TreeChecker.scala:106)
[error] dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:324)
[error] scala.collection.immutable.List.map(List.scala:246)
[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.compileUnits(Run.scala:201)
[error] dotty.tools.dotc.Driver.finish(Driver.scala:56)
[error] dotty.tools.dotc.Driver.doCompile(Driver.scala:36)
[error] dotty.tools.xsbt.CompilerBridgeDriver.run(CompilerBridgeDriver.java:88)
[error] dotty.tools.xsbt.CompilerBridge.run(CompilerBridge.java:22)

Note that I'm not entirely certain whether extracting a subtree of an Inlined node using Inlined(_, _, Block(List(Apply(_, List(rhs))), _)) is legal, but I think it should be (how else would you get the tree to process it further).

Metadata

Metadata

Assignees

Labels

area:metaprogramming:reflectionIssues related to the quotes reflection APIarea:private optionsIssues tied to -Y private/internal compiler settings.itype:bugitype:crashstat:changed in nextIssues for which the behaviour is different in Scala Next, but the issue remains valid there.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions