Skip to content

inserting EmptyTree when recovering from StopMacroException can lead to AssertionError instead of user message #14039

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

Closed
scf37 opened this issue Dec 4, 2021 · 4 comments · Fixed by #14046

Comments

@scf37
Copy link

scf37 commented Dec 4, 2021

From what I can see in debugger, macro throws StopMacroException, then Splicer catches it and inserts EmptyTree at Splicer.scala:70. But resulting AST is incorrect and leads to further AssertionError without emitting user error.

Compiler version

3.1.0

Minimized code

  inline def apply[A, Request, Response](method: String, uri: String, handler: A, methodName: String, modifiers: Seq[RouteModifier]): Endpoint[Request, Response] =
    ${Macro.impl[A, Request, Response]('method, 'uri, 'handler, 'methodName, 'modifiers)}

  def impl[A: Type, Request: Type, Response: Type](methodExpr: Expr[String], uriExpr: Expr[String], handlerExpr: Expr[A], methodNameExpr: Expr[String], modifiersExpr: Expr[Seq[RouteModifier]])(using Quotes): Expr[Endpoint[Request, Response]] = {
    import quotes.reflect.*
   report.errorAndAbort("my message")
  }

// macro invocation code
trait Dsl[Request, Response] {
  val entries = Seq.newBuilder[Endpoint[Request, Response]]

  protected inline def get[A](inline uri: String, handler: A, methodName: String, modifiers: RouteModifier*): Unit = entries += Macro[A, Request, Response]("get", uri, handler, methodName, modifiers)
  protected inline def post[A](inline uri: String, handler: A, methodName: String, modifiers: RouteModifier*): Unit = entries += Macro[A, Request, Response]("post", uri, handler, methodName, modifiers)
}

Output (click arrow to expand)

Exception in thread "main" java.lang.AssertionError: assertion failed: Select(TypedSplice(Select(Inlined(EmptyTree,List(),Ident(Dsl_this)),entries)),+)
	at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
	at dotty.tools.dotc.typer.Inliner$InlineTyper.typedSelect(Inliner.scala:1503)
	at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:2725)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2817)
	at dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:121)
	at dotty.tools.dotc.typer.Inliner$InlineTyper.typedUnadapted(Inliner.scala:1634)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2883)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2887)
	at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3003)
	at dotty.tools.dotc.typer.Applications.realApply$1(Applications.scala:885)
	at dotty.tools.dotc.typer.Applications.typedApply(Applications.scala:1035)
	at dotty.tools.dotc.typer.Applications.typedApply$(Applications.scala:317)
	at dotty.tools.dotc.typer.Inliner$InlineTyper.typedApply(Inliner.scala:1547)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2755)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2818)
	at dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:121)
	at dotty.tools.dotc.typer.Inliner$InlineTyper.typedUnadapted(Inliner.scala:1634)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2883)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2887)
	at dotty.tools.dotc.typer.Typer.reassignmentToVal$1(Typer.scala:954)
	at dotty.tools.dotc.typer.Typer.typedAssign(Typer.scala:1015)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2762)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2818)
	at dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:121)
	at dotty.tools.dotc.typer.Inliner$InlineTyper.typedUnadapted(Inliner.scala:1634)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2883)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2887)
	at dotty.tools.dotc.typer.Applications.typedOpAssign$1(Applications.scala:1017)
	at dotty.tools.dotc.typer.Applications.$anonfun$22$$anonfun$1(Applications.scala:1026)
	at dotty.tools.dotc.typer.Typer.tryEither(Typer.scala:3011)
	at dotty.tools.dotc.typer.Applications.$anonfun$5(Applications.scala:1030)
	at dotty.tools.dotc.typer.Typer.tryEither(Typer.scala:3014)
	at dotty.tools.dotc.typer.Applications.typedApply(Applications.scala:1031)
	at dotty.tools.dotc.typer.Applications.typedApply$(Applications.scala:317)
	at dotty.tools.dotc.typer.Inliner$InlineTyper.typedApply(Inliner.scala:1547)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2755)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2818)
	at dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:121)
	at dotty.tools.dotc.typer.Inliner$InlineTyper.typedUnadapted(Inliner.scala:1634)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2883)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2887)
	at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:2936)
	at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:2959)
	at dotty.tools.dotc.typer.Typer.typedBlockStats(Typer.scala:1027)
	at dotty.tools.dotc.typer.Typer.typedBlock(Typer.scala:1031)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2763)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2818)
	at dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:121)
	at dotty.tools.dotc.typer.Inliner$InlineTyper.typedUnadapted(Inliner.scala:1634)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2883)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2887)
	at dotty.tools.dotc.typer.ReTyper.typedTyped(ReTyper.scala:62)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2760)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2818)
	at dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:121)
	at dotty.tools.dotc.typer.Inliner$InlineTyper.typedUnadapted(Inliner.scala:1634)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2883)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2880)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2887)
	at dotty.tools.dotc.typer.Inliner.inlined(Inliner.scala:1019)
	at dotty.tools.dotc.typer.Inliner$.inlineCall(Inliner.scala:155)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:85)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.traverse$1(TreeMapWithImplicits.scala:53)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transformStats(TreeMapWithImplicits.scala:60)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:111)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:79)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1473)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:120)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:79)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.traverse$1(TreeMapWithImplicits.scala:53)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transformStats(TreeMapWithImplicits.scala:60)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:111)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:79)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1473)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:120)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:79)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.traverse$1(TreeMapWithImplicits.scala:53)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transformStats(TreeMapWithImplicits.scala:60)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1484)
	at dotty.tools.dotc.ast.TreeMapWithImplicits.transform(TreeMapWithImplicits.scala:120)
	at dotty.tools.dotc.transform.Inlining$InliningTreeMap.transform(Inlining.scala:93)
	at dotty.tools.dotc.transform.Inlining$$anon$2.transform(Inlining.scala:71)
	at dotty.tools.dotc.transform.MacroTransform.run(MacroTransform.scala:21)
	at dotty.tools.dotc.transform.Inlining.run(Inlining.scala:43)
	at dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:308)
	at scala.collection.immutable.List.map(List.scala:246)
	at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:309)
	at dotty.tools.dotc.transform.Inlining.runOn(Inlining.scala:47)
	at dotty.tools.dotc.Run.runPhases$4$$anonfun$4(Run.scala:261)
	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:1323)
	at dotty.tools.dotc.Run.runPhases$5(Run.scala:272)
	at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:280)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
	at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:68)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:289)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:228)
	at dotty.tools.repl.ReplCompiler.runCompilationUnit(ReplCompiler.scala:155)
	at dotty.tools.repl.ReplCompiler.compile(ReplCompiler.scala:165)
	at dotty.tools.repl.ReplDriver.compile(ReplDriver.scala:250)
	at dotty.tools.repl.ReplDriver.interpret(ReplDriver.scala:218)
	at dotty.tools.repl.ReplDriver.run$$anonfun$1(ReplDriver.scala:161)
	at dotty.tools.repl.ReplDriver.withRedirectedOutput(ReplDriver.scala:174)
	at dotty.tools.repl.ReplDriver.run(ReplDriver.scala:162)
	at dotty.tools.repl.ScriptEngine.eval(ScriptEngine.scala:38)
	at java.scripting/javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
	at Main$package$.main(Main.scala:51)
	at main.main(Main.scala:44)
@scf37
Copy link
Author

scf37 commented Dec 4, 2021

Problem is with +=, following macro invocation works as expected:

    val v = Macro[A, Request, Response]("get", uri, handler, methodName, modifiers)
    entries += v

@nicolasstucki
Copy link
Contributor

Complete self contained example

import scala.quoted.*

object Macro:
  inline def apply[A, Request, Response](method: String, uri: String, handler: A, methodName: String, modifiers: Seq[RouteModifier]): Endpoint[Request, Response] =
    ${Macro.impl[A, Request, Response]('method, 'uri, 'handler, 'methodName, 'modifiers)}

  def impl[A: Type, Request: Type, Response: Type](methodExpr: Expr[String], uriExpr: Expr[String], handlerExpr: Expr[A], methodNameExpr: Expr[String], modifiersExpr: Expr[Seq[RouteModifier]])(using Quotes): Expr[Endpoint[Request, Response]] = {
    import quotes.reflect.*
    report.errorAndAbort("my message")
  }
trait Endpoint[T, U]
type RouteModifier = String

trait Dsl[Request, Response] {
  val entries = Seq.newBuilder[Endpoint[Request, Response]]

  inline def get[A](inline uri: String, handler: A, methodName: String, modifiers: RouteModifier*): Unit =
    entries += Macro[A, Request, Response]("get", uri, handler, methodName, modifiers)
  protected inline def post[A](inline uri: String, handler: A, methodName: String, modifiers: RouteModifier*): Unit =
    entries += Macro[A, Request, Response]("post", uri, handler, methodName, modifiers)
  def test = get("", "", "", "")
}

@nicolasstucki
Copy link
Contributor

nicolasstucki commented Dec 6, 2021

Minimized

import scala.quoted.*

object Macro:
  inline def apply(): Any = ${Macro.impl}

  def impl(using Quotes): Expr[Any] =
    quotes.reflect.report.errorAndAbort("my message")
class Dsl {
  val entries = Seq.newBuilder[Any]
  inline def get(): Unit = entries += Macro.apply()
  def test = get()
}

@nicolasstucki
Copy link
Contributor

Minimized witouth macros

val entries = Seq.newBuilder[Any]
inline def error(): Any = compiletime.error("")
inline def get(): Unit = entries += error()
def test = get()

nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Dec 6, 2021
This assertion can fail if the inlined function generated a error.

Fixes scala#14039
olsdavis pushed a commit to olsdavis/dotty that referenced this issue Apr 4, 2022
This assertion can fail if the inlined function generated a error.

Fixes scala#14039
@Kordyjan Kordyjan added this to the 3.1.2 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants