Skip to content

"Missing outer accessor" assertion failure when using plugin with inline #23793

@adkian-sifive

Description

@adkian-sifive

Compiler version

3.3.5, 3.7.2

Explanation

Encountered while migrating the Chisel language to Scala 3. In Chisel, we use plugins to insert typed ASTs to certain user-facing types like Bundles.

In the minimized plugin code below, a plugin is adding a method called _doNothing to all subtypes of Okable. In the test code, we're passing an anonymous Okable subtype by-name to the inline method foo. This construction causes a compiler crash during erasure when the compiler attempts to create a path to _doNothing.

Minimized code

Plugin with Scala-CLI header

plugin.scala

//> using scala "3.7.2"
//> using dep "org.scala-lang::scala3-compiler:3.7.2"
//> using resourceDir "resources"

import dotty.tools.dotc.report
import dotty.tools.dotc.plugins.{PluginPhase, StandardPlugin}
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.tpd.Tree
import dotty.tools.dotc.ast.tpd.TreeOps
import dotty.tools.dotc.CompilationUnit
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.typer.TyperPhase
import dotty.tools.dotc.parsing._

import dotty.tools.dotc.core.Names
import dotty.tools.dotc.core.Flags
import dotty.tools.dotc.core.Constants.*
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.core.Types.*

class MyPluginPhase extends PluginPhase {
  val phaseName: String = "MyPluginPhase"
  override val runsAfter = Set(TyperPhase.name)

  def isOkable(t: Type)(using Context): Boolean = {
    val okableTpe = requiredClass("bug.Okable")
    t.baseClasses.contains(okableTpe)
  }

  def genOkable(outer: tpd.TypeDef)(using ctx: Context): tpd.DefDef = {
    val newExpr = tpd.New(outer.symbol.typeRef, Nil)
    val okableTpe = requiredClassRef("bug.Okable")
    val okableSym = newSymbol(
      outer.symbol,
      Names.termName("_makeOkable"),
      Flags.Method | Flags.Override,
      MethodType(Nil, Nil, okableTpe)
    )

    tpd.DefDef(okableSym.asTerm, _ => newExpr)
  }

  override def transformTypeDef(okable: tpd.TypeDef)(using Context): tpd.Tree = {
    if (isOkable(okable.tpe) && okable.isClassDef
      && !okable.symbol.flags.is(Flags.Abstract)) {

      val thiz:     tpd.This = tpd.This(okable.symbol.asClass)
      val printsOkDef = genOkable(okable)

      okable match {
        case td @ tpd.TypeDef(name, tmpl: tpd.Template) => {
          val newDefs = printsOkDef.toList
          val newTemplate =
            if (tmpl.body.size >= 1)
              cpy.Template(tmpl)(body = newDefs ++: tmpl.body)
            else
              cpy.Template(tmpl)(body = newDefs)
          tpd.cpy.TypeDef(td)(name, newTemplate)
        }
        case _ => super.transformTypeDef(okable)
      }
    } else {
      super.transformTypeDef(okable)
    }
  }
}

class MyPlugin extends StandardPlugin {
  val name: String = "MyPlugin"
  override val description: String = "MyPlugin"

  override def init(options: List[String]): List[PluginPhase] = {
    (new MyPluginPhase) :: Nil
  }
}

Create a resource file with:

mkdir -p resources
echo "pluginClass=MyPlugin" > resources/plugin.properties

Package with Scala-CLI:

scala-cli --power package plugin.scala -o plugin.jar --assembly --preamble=false

Test code:

test.scala

package bug

abstract class Okable {
  def _makeOkable: Okable = ???
} 

// Plugin adds a _makeOkable override for all Okable
abstract class SomeClass extends Okable
class SomeChildClass extends SomeClass

class Test {
  inline def foo[A](a: => A): A = a

  foo {
    val sc = new SomeClass {
      val scc = new SomeChildClass
    }
  }
}

Compile with (crashes)

scalac -Xplugin:plugin.jar test.scala

Output (click arrow to expand)

  unhandled exception while running erasure on test.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: test.scala
        during phase: erasure
                mode: Mode(ImplicitsEnabled)
     library version: version 2.13.15
    compiler version: version 3.6.2
            settings: -Vprint List(MyPlugin) -Xplugin List(plugin.jar)

Exception in thread "main" java.lang.AssertionError: assertion failed: missing outer accessor in class Okable
	at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
	at dotty.tools.dotc.transform.ExplicitOuter$.dotty$tools$dotc$transform$ExplicitOuter$$$outerParamAccessor(ExplicitOuter.scala:236)
	at dotty.tools.dotc.transform.ExplicitOuter$OuterOps$.loop$1(ExplicitOuter.scala:460)
	at dotty.tools.dotc.transform.ExplicitOuter$OuterOps$.path$extension(ExplicitOuter.scala:469)
	at dotty.tools.dotc.transform.Erasure$Typer.typedThis(Erasure.scala:814)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3497)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3581)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3658)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
	at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3773)
	at dotty.tools.dotc.transform.Erasure$Typer.$anonfun$7(Erasure.scala:866)
	at dotty.tools.dotc.core.Decorators$.zipWithConserve(Decorators.scala:160)
	at dotty.tools.dotc.transform.Erasure$Typer.typedApply(Erasure.scala:866)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3496)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3581)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3658)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
	at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3773)
	at dotty.tools.dotc.typer.Typer.$anonfun$66(Typer.scala:2897)
	at dotty.tools.dotc.inlines.PrepareInlineable$.dropInlineIfError(PrepareInlineable.scala:256)
	at dotty.tools.dotc.typer.Typer.typedDefDef(Typer.scala:2897)
	at dotty.tools.dotc.transform.Erasure$Typer.typedDefDef(Erasure.scala:972)
	at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:3478)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3580)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3658)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
	at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3684)
	at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3730)
	at dotty.tools.dotc.transform.Erasure$Typer.typedStats(Erasure.scala:1085)
	at dotty.tools.dotc.typer.Typer.typedClassDef(Typer.scala:3160)
	at dotty.tools.dotc.transform.Erasure$Typer.typedClassDef(Erasure.scala:1061)
	at dotty.tools.dotc.typer.Typer.typedTypeOrClassDef$1(Typer.scala:3484)
	at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:3488)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3580)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3658)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
	at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3684)
	at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3730)
	at dotty.tools.dotc.transform.Erasure$Typer.typedStats(Erasure.scala:1085)
	at dotty.tools.dotc.typer.Typer.typedBlockStats(Typer.scala:1427)
	at dotty.tools.dotc.typer.Typer.typedBlock(Typer.scala:1431)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3504)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3581)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3658)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
	at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3773)
	at dotty.tools.dotc.typer.Typer.typedValDef(Typer.scala:2834)
	at dotty.tools.dotc.transform.Erasure$Typer.typedValDef(Erasure.scala:923)
	at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:3475)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3580)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3658)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
	at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3684)
	at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3730)
	at dotty.tools.dotc.transform.Erasure$Typer.typedStats(Erasure.scala:1085)
	at dotty.tools.dotc.typer.Typer.typedBlockStats(Typer.scala:1427)
	at dotty.tools.dotc.typer.Typer.typedBlock(Typer.scala:1431)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3504)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3581)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3658)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
	at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3773)
	at dotty.tools.dotc.typer.Typer.$anonfun$66(Typer.scala:2897)
	at dotty.tools.dotc.inlines.PrepareInlineable$.dropInlineIfError(PrepareInlineable.scala:256)
	at dotty.tools.dotc.typer.Typer.typedDefDef(Typer.scala:2897)
	at dotty.tools.dotc.transform.Erasure$Typer.typedDefDef(Erasure.scala:972)
	at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:3478)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3580)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3658)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
	at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3684)
	at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3730)
	at dotty.tools.dotc.transform.Erasure$Typer.typedStats(Erasure.scala:1085)
	at dotty.tools.dotc.typer.Typer.typedBlockStats(Typer.scala:1427)
	at dotty.tools.dotc.typer.ReTyper.typedInlined(ReTyper.scala:99)
	at dotty.tools.dotc.transform.Erasure$Typer.typedInlined(Erasure.scala:914)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3519)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3581)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3658)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
	at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3711)
	at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3730)
	at dotty.tools.dotc.transform.Erasure$Typer.typedStats(Erasure.scala:1085)
	at dotty.tools.dotc.typer.Typer.typedClassDef(Typer.scala:3160)
	at dotty.tools.dotc.transform.Erasure$Typer.typedClassDef(Erasure.scala:1061)
	at dotty.tools.dotc.typer.Typer.typedTypeOrClassDef$1(Typer.scala:3484)
	at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:3488)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3580)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3658)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
	at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:3684)
	at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:3730)
	at dotty.tools.dotc.transform.Erasure$Typer.typedStats(Erasure.scala:1085)
	at dotty.tools.dotc.typer.Typer.typedPackageDef(Typer.scala:3293)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:3530)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:3581)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3658)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:3662)
	at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:3773)
	at dotty.tools.dotc.transform.Erasure.run(Erasure.scala:146)
	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:384)
	at dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:396)
	at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:69)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:396)
	at dotty.tools.dotc.Run.compileSources(Run.scala:282)
	at dotty.tools.dotc.Run.compile(Run.scala:267)
	at dotty.tools.dotc.Driver.doCompile(Driver.scala:37)
	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.MainGenericCompiler$.run$1(MainGenericCompiler.scala:162)
	at dotty.tools.MainGenericCompiler$.main(MainGenericCompiler.scala:186)
	at dotty.tools.MainGenericCompiler.main(MainGenericCompiler.scala)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at coursier.bootstrap.launcher.a.a(Unknown Source)
	at coursier.bootstrap.launcher.Launcher.main(Unknown Source)

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions