Skip to content

Illegal Array type in inline macros can cause crashes with -Ycheck #22034

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

Open
jchyb opened this issue Nov 26, 2024 · 0 comments
Open

Illegal Array type in inline macros can cause crashes with -Ycheck #22034

jchyb opened this issue Nov 26, 2024 · 0 comments
Labels
area:metaprogramming:other Issues tied to metaprogramming/macros not covered by the other labels. itype:bug

Comments

@jchyb
Copy link
Contributor

jchyb commented Nov 26, 2024

Compiler version

any

Minimized code

Macro_1.scala:

import scala.quoted._
object Macro:

  inline def test() = ${testImpl}

  def testImpl(using Quotes): Expr[Any] = {
    import quotes.reflect._
    val tpe = TypeRepr.of[Array[Byte]] match
      case AppliedType(tycons, _) => tycons
    tpe.asType match
      case '[t] => '{ List[t]() }
  }

  inline def recurTest() = ${recurTestImpl}
  def recurTestImpl(using Quotes): Expr[Any] = {
    import quotes.reflect._
    val tpe = TypeRepr.of[Array[Byte]] match
      case AppliedType(tycons, _) => AppliedType(tycons, List(tycons))
    tpe.asType match
      case '[t] =>
        '{ List[t]() }
  }

Test_2.scala

@main def Test: Unit =
  Macro.test() // error
  Macro.recurTest() // error

Output

*** error while checking i21916-b/Test_2.scala after phase erasure ***

  unhandled exception while running Ycheck on i21916-b/Test_2.scala

  An unhandled exception was thrown in the compiler.
  Please file a crash report here:
  https://github.com/scala/scala3/issues/new/choose
  For non-enriched exceptions, compile with -Xno-enrich-error-messages.


     while compiling: i21916-b/Test_2.scala
        during phase: Ycheck
                mode: Mode(ImplicitsEnabled)
     library version: version 2.13.15
    compiler version: version 3.6.4-RC1-bin-SNAPSHOT-nonbootstrapped-git-164a8ed
            settings: -Ycheck List(all) -classpath /Users/jchyb/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.15/scala-library-2.13.15.jar:/Users/jchyb/workspace/scala3/library/../out/bootstrap/scala3-library-bootstrapped/scala-3.6.4-RC1-bin-SNAPSHOT-nonbootstrapped/scala3-library_3-3.6.4-RC1-bin-SNAPSHOT.jar
Exception in thread "main" java.lang.AssertionError: assertion failed: The type Array - TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Array) of class class dotty.tools.dotc.core.Types$CachedTypeRef of tree Array : Array / class dotty.tools.dotc.ast.Trees$TypeTree is illegal after erasure, phase = erasure

  Exception while compiling i21916-b/Test_2.scala, i21916-b/Macro_1.scala

  An unhandled exception was thrown in the compiler.
  Please file a crash report here:
  https://github.com/scala/scala3/issues/new/choose
  For non-enriched exceptions, compile with -Xno-enrich-error-messages.


     while compiling: <no file>
        during phase: parser
                mode: Mode()
     library version: version 2.13.15
    compiler version: version 3.6.4-RC1-bin-SNAPSHOT-nonbootstrapped-git-164a8ed
            settings: -Ycheck List(all) -classpath /Users/jchyb/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.15/scala-library-2.13.15.jar:/Users/jchyb/workspace/scala3/library/../out/bootstrap/scala3-library-bootstrapped/scala-3.6.4-RC1-bin-SNAPSHOT-nonbootstrapped/scala3-library_3-3.6.4-RC1-bin-SNAPSHOT.jar
        at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
        at dotty.tools.dotc.transform.Erasure.assertErased(Erasure.scala:202)
        at dotty.tools.dotc.transform.Erasure.assertErased(Erasure.scala:179)
        at dotty.tools.dotc.transform.Erasure.checkPostCondition(Erasure.scala:156)
        at dotty.tools.dotc.transform.TreeChecker$.dotty$tools$dotc$transform$TreeChecker$Checker$$_$typedUnadapted$$anonfun$1(TreeChecker.scala:439)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
        at scala.collection.immutable.List.foreach(List.scala:334)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:439)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3659)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3663)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:416)
        at dotty.tools.dotc.typer.Typer.typedSeqLiteral(Typer.scala:2387)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3519)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3582)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:434)
        at dotty.tools.dotc.typer.ProtoTypes$FunProto.$anonfun$7(ProtoTypes.scala:543)
        at dotty.tools.dotc.typer.ProtoTypes$FunProto.cacheTypedArg(ProtoTypes.scala:466)
        at dotty.tools.dotc.typer.ProtoTypes$FunProto.typedArg(ProtoTypes.scala:544)
        at dotty.tools.dotc.typer.Applications$ApplyToUntyped.typedArg(Applications.scala:1007)
        at dotty.tools.dotc.typer.Applications$ApplyToUntyped.typedArg(Applications.scala:1007)
        at dotty.tools.dotc.typer.Applications$Application.addTyped$1(Applications.scala:688)
        at dotty.tools.dotc.typer.Applications$Application.matchArgs(Applications.scala:756)
        at dotty.tools.dotc.typer.Applications$Application.init(Applications.scala:574)
        at dotty.tools.dotc.typer.Applications$TypedApply.<init>(Applications.scala:882)
        at dotty.tools.dotc.typer.Applications$ApplyToUntyped.<init>(Applications.scala:1006)
        at dotty.tools.dotc.typer.Applications.ApplyTo(Applications.scala:1270)
        at dotty.tools.dotc.typer.Applications.ApplyTo$(Applications.scala:434)
        at dotty.tools.dotc.typer.Typer.ApplyTo(Typer.scala:148)
        at dotty.tools.dotc.typer.Applications.simpleApply$1(Applications.scala:1079)
        at dotty.tools.dotc.typer.Applications.realApply$1$$anonfun$2(Applications.scala:1189)
        at dotty.tools.dotc.typer.Typer$.tryEither(Typer.scala:121)
        at dotty.tools.dotc.typer.Applications.realApply$1(Applications.scala:1204)
        at dotty.tools.dotc.typer.Applications.typedApply(Applications.scala:1244)
        at dotty.tools.dotc.typer.Applications.typedApply$(Applications.scala:434)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedApply(TreeChecker.scala:543)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3497)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3582)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:434)
        at dotty.tools.dotc.typer.ProtoTypes$FunProto.$anonfun$7(ProtoTypes.scala:543)
        at dotty.tools.dotc.typer.ProtoTypes$FunProto.cacheTypedArg(ProtoTypes.scala:466)
        at dotty.tools.dotc.typer.ProtoTypes$FunProto.typedArg(ProtoTypes.scala:544)
        at dotty.tools.dotc.typer.Applications$ApplyToUntyped.typedArg(Applications.scala:1007)
        at dotty.tools.dotc.typer.Applications$ApplyToUntyped.typedArg(Applications.scala:1007)
        at dotty.tools.dotc.typer.Applications$Application.addTyped$1(Applications.scala:688)
        at dotty.tools.dotc.typer.Applications$Application.matchArgs(Applications.scala:756)
        at dotty.tools.dotc.typer.Applications$Application.init(Applications.scala:574)
        at dotty.tools.dotc.typer.Applications$TypedApply.<init>(Applications.scala:882)
        at dotty.tools.dotc.typer.Applications$ApplyToUntyped.<init>(Applications.scala:1006)
        at dotty.tools.dotc.typer.Applications.ApplyTo(Applications.scala:1270)
        at dotty.tools.dotc.typer.Applications.ApplyTo$(Applications.scala:434)
        at dotty.tools.dotc.typer.Typer.ApplyTo(Typer.scala:148)
        at dotty.tools.dotc.typer.Applications.simpleApply$1(Applications.scala:1079)
        at dotty.tools.dotc.typer.Applications.realApply$1$$anonfun$2(Applications.scala:1189)
        at dotty.tools.dotc.typer.Typer$.tryEither(Typer.scala:121)
        at dotty.tools.dotc.typer.Applications.realApply$1(Applications.scala:1204)
        at dotty.tools.dotc.typer.Applications.typedApply(Applications.scala:1244)
        at dotty.tools.dotc.typer.Applications.typedApply$(Applications.scala:434)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedApply(TreeChecker.scala:543)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3497)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3582)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:434)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3659)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3663)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:416)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedTyped(TreeChecker.scala:563)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3502)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3582)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:434)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3659)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3663)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:416)
        at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3712)
        at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3731)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedStats(TreeChecker.scala:691)
        at dotty.tools.dotc.typer.Typer.typedBlockStats(Typer.scala:1430)
        at dotty.tools.dotc.typer.Typer.typedBlock(Typer.scala:1434)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedBlock$$anonfun$1$$anonfun$1(TreeChecker.scala:673)
        at dotty.tools.dotc.transform.TreeChecker$Checker.withDefinedSyms(TreeChecker.scala:278)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedBlock$$anonfun$1(TreeChecker.scala:673)
        at dotty.tools.dotc.transform.TreeChecker$Checker.withBlock(TreeChecker.scala:306)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedBlock(TreeChecker.scala:673)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3505)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3582)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:434)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3659)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3663)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:416)
        at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3774)
        at dotty.tools.dotc.typer.Typer.$anonfun$66(Typer.scala:2898)
        at dotty.tools.dotc.inlines.PrepareInlineable$.dropInlineIfError(PrepareInlineable.scala:256)
        at dotty.tools.dotc.typer.Typer.typedDefDef(Typer.scala:2898)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedDefDef$$anonfun$1(TreeChecker.scala:648)
        at dotty.tools.dotc.transform.TreeChecker$Checker.withDefinedSyms(TreeChecker.scala:278)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedDefDef(TreeChecker.scala:651)
        at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:3479)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3581)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:434)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3659)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3663)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:416)
        at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3685)
        at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3731)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedStats(TreeChecker.scala:691)
        at dotty.tools.dotc.typer.Typer.typedClassDef(Typer.scala:3164)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedClassDef(TreeChecker.scala:618)
        at dotty.tools.dotc.typer.Typer.typedTypeOrClassDef$1(Typer.scala:3485)
        at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:3489)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3581)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:434)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3659)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3663)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:416)
        at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3685)
        at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3731)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedStats(TreeChecker.scala:691)
        at dotty.tools.dotc.typer.Typer.typedPackageDef(Typer.scala:3297)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedPackageDef(TreeChecker.scala:717)
        at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3531)
        at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3582)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:434)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3659)
        at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3663)
        at dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:416)
        at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3774)
        at dotty.tools.dotc.transform.TreeChecker.check(TreeChecker.scala:131)
        at dotty.tools.dotc.transform.TreeChecker.run(TreeChecker.scala:111)
        at dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:380)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
        at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
        at scala.collection.immutable.List.foreach(List.scala:334)
        at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:373)
        at dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:343)
        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$1(Run.scala:336)
        at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:383)
        at dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:395)
        at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:69)
        at dotty.tools.dotc.Run.compileUnits(Run.scala:395)
        at dotty.tools.dotc.Run.compileUnits(Run.scala:288)
        at dotty.tools.dotc.Run.compileSuspendedUnits(Run.scala:409)
        at dotty.tools.dotc.Driver.finish(Driver.scala:63)
        at dotty.tools.dotc.Driver.doCompile(Driver.scala:38)
        at dotty.tools.dotc.Driver.process(Driver.scala:201)
        at dotty.tools.dotc.Driver.process(Driver.scala:169)
        at dotty.tools.dotc.Driver.process(Driver.scala:181)
        at dotty.tools.dotc.Driver.main(Driver.scala:211)
        at dotty.tools.dotc.Main.main(Main.scala)

Expectation

No crash. Instead, an error message from -Xcheck-macros or -Ycheck:all. The issue here lies with an incorrect macro implementation causing the generated code to have a parameterless Array in an illegal spot. If this was a transparent inline macro, this would be caught and reported by a PostTyper check. Perhaps the best fix here is implementing a similar -Xcheck-macros check, however it in my opinion should be limited to just checking a correct Array construction. Otherwise it could cause regressions in the ecosystem (as far I could see, other parameterless type constructors will not cause crashes like this anyway).

@jchyb jchyb added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label area:metaprogramming:other Issues tied to metaprogramming/macros not covered by the other labels. and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Nov 26, 2024
hamzaremmal added a commit that referenced this issue Feb 21, 2025
…#22033)

Closes #21916
I tried to supply the ClassOfConstant with multiple other broken Types,
but I was unable to break it beyond the linked issue, so I ended up
adding the check for only that one case. This makes sense - the backend
(and thus erasure) needs to know if the Array type parameter is a
primitive type, but in other cases the erasure phase needs to know only
the class, without the type parameters.

It's impossible to call classOf through the quoted code (`'{classOf[t]}`
with a boundless t will error out), so we don't need that additional
check there.

There does appear to be an issue with being able to set `'{List[Array]}`
resulting in a crash, but that is beyond the scope of this fix - I will
prepare a separate issue for that (edit: reported
[here](#22034)).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:metaprogramming:other Issues tied to metaprogramming/macros not covered by the other labels. itype:bug
Projects
None yet
Development

No branches or pull requests

1 participant