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

Automatic Type Derivation causes Assertion Error for Non-Static Enum #11174

Closed
deusaquilus opened this issue Jan 20, 2021 · 4 comments · Fixed by #15847
Closed

Automatic Type Derivation causes Assertion Error for Non-Static Enum #11174

deusaquilus opened this issue Jan 20, 2021 · 4 comments · Fixed by #15847

Comments

@deusaquilus
Copy link
Contributor

Minimized code

First, let's implement a really simple type derivation that just prints something for every given type in a product/coproduct

object EnumerateNames {

  inline def summonNext[T] =
    summonFrom {
      case en: EnumerateNames[T] => en
    }

  inline def walkThrough[Types <: Tuple]: List[String] =
    inline erasedValue[Types] match
      case _: (tpe *: tpes) =>
        summonNext[tpe].apply +: walkThrough[tpes]
      case _: EmptyTuple =>
        Nil
      

  inline def derived[T]: EnumerateNames[T] = 
    summonFrom {
      case ev: Mirror.Of[T] =>
        new EnumerateNames[T] {
          def apply =
            inline ev match
              case m: Mirror.ProductOf[T] => walkThrough[m.MirroredElemTypes].mkString(", ")
              case m: Mirror.SumOf[T] => walkThrough[m.MirroredElemTypes].mkString(", ")
        }
    }
}

trait EnumerateNames[T] {
  def apply: String
}

Then let's create a class that defines an enum and an automatic derivation for types:

class MainClass {
  enum Shape:
    case Square(width: Int, height: Int) extends Shape
    case Circle(radius: Int) extends Shape

  given EnumerateNames[Int] with {
    def apply: String = "int"
  }
  inline given auto[T]:EnumerateNames[T] = EnumerateNames.derived
  def deriveEnumerateNames[T](using en: EnumerateNames[T]) = en.apply  
  def run: Unit = println( deriveEnumerateNames[Shape] )
}

Then let's call that class:

object Main {
  def main(args: Array[String]) = {
    new MainClass().run
  }
} 

Output

A very large error happens:

exception while typing MainClass.this.Shape$.this of class class dotty.tools.dotc.ast.Trees$This # -1
exception while typing MainClass.this.Shape$.this.Square of class class dotty.tools.dotc.ast.Trees$Select # -1
exception while typing MainClass.this.Shape$.this.Square:
  
    (
      deriving.Mirror{
        MirroredType = io.getquill.MainClass.Shape.Square; 
          MirroredMonoType = io.getquill.MainClass.Shape.Square
        ; MirroredElemTypes <: Tuple
      }
     & 
      scala.deriving.Mirror.Product{
        MirroredMonoType = io.getquill.MainClass.Shape.Square; 
          MirroredType = io.getquill.MainClass.Shape.Square
        ; MirroredLabel = ("Square" : String)
      }
    ){
...
java.lang.AssertionError: assertion failed: failure to construct path from value ev/value elem$1/method apply/anonymous class Object with io.getquill.EnumerateNames {...}/method run/class MainClass/package io.getquill/package io/package <root> to `this` of object Shape in class MainClass;
class MainClass in package io.getquill does not have an outer accessor while compiling /Users/aleiof/git/enum_summon/src/main/scala/io/getquill/Example.scala, /Users/aleiof/git/enum_summon/src/main/scala/io/getquill/Main.scala
[error] ## Exception when compiling 2 sources to /Users/aleiof/git/enum_summon/target/scala-3.0.0-M3/classes
[error] java.lang.AssertionError: assertion failed: failure to construct path from value ev/value elem$1/method apply/anonymous class Object with io.getquill.EnumerateNames {...}/method run/class MainClass/package io.getquill/package io/package <root> to `this` of object Shape in class MainClass;
[error] class MainClass in package io.getquill does not have an outer accessor
[error] scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
[error] dotty.tools.dotc.transform.ExplicitOuter$OuterOps$.loop$1(ExplicitOuter.scala:410
[error] dotty.tools.dotc.transform.ExplicitOuter$OuterOps$.path$extension(ExplicitOuter.scala:429)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedThis(Erasure.scala:750)
[error] dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2529)
[error] dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2590)
[error] dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:118)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:2659)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:2663)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedSelect(Erasure.scala:641)
[error] dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:2498)
[error] dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2589)
[error] dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:118)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:2659)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:2663)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedTyped(Erasure.scala:603)
[error] dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2533)
[error] dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2590)
[error] dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:118)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:2659)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:2663)
[error] dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:2779)
[error] dotty.tools.dotc.transform.Erasure$Typer.typedTypeApply(Erasure.scala:767)
[error] dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:2547)
[error] dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2590)
[error] dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:118)
[error] dotty.tools.dotc.typer.Typer.typed(Typer.scala:2659)
...
[error] (Compile / compileIncremental) java.lang.AssertionError: assertion failed: failure to construct path from value ev/value elem$1/method apply/anonymous class Object with io.getquill.EnumerateNames {...}/method run/class MainClass/package io.getquill/package io/package <root> to `this` of object Shape in class MainClass;
[error] class MainClass in package io.getquill does not have an outer accessor
[error] Total time: 1 s, completed Jan 20, 2021 2:24:19 AM

Expectation

Compilation should work and "int, int, int" should be returned.

Workaround

Moving the enum to a static place (e.g. into an object) seems to solve the problem.

Repo

Repository with code sample can be found here:
https://github.com/deusaquilus/enum_summon

@deusaquilus deusaquilus changed the title Automatic Type Derivation fails for Non-Static Enum Automatic Type Derivation causes Assertion Error for Non-Static Enum Jan 20, 2021
@deusaquilus
Copy link
Contributor Author

deusaquilus commented Jan 20, 2021

It will also break if you define Shape in the run method:

class MainClass {
  given EnumerateNames[Int] with {
    def apply: String = "int"
  }
  inline given auto[T]:EnumerateNames[T] = EnumerateNames.derived
  def deriveEnumerateNames[T](using en: EnumerateNames[T]) = en.apply

  def run = {
    enum Shape:
      case Square(width: Int, height: Int) extends Shape
      case Circle(radius: Int) extends Shape
    
    println( deriveEnumerateNames[Shape] )
  }
}

@nicolasstucki
Copy link
Contributor

Minimized

import scala.compiletime.{summonFrom, summonInline, erasedValue}
import scala.deriving.Mirror

trait EnumerateNames[T]

object EnumerateNames {
  inline def derived[T]: EnumerateNames[T] =
    summonFrom {
      case ev: Mirror.Of[T] =>
        inline ev match
          case m: Mirror.ProductOf[T] => ???
          case m: Mirror.SumOf[T] =>
            inline erasedValue[m.MirroredElemTypes] match
              case _: (tpe *: _) => summonInline[EnumerateNames[tpe]]
              case _: EmptyTuple =>
        ???
    }
}

class MainClass {
  enum Shape:
    case Point
  inline given auto[T]: EnumerateNames[T] = EnumerateNames.derived[T]
  def shapeNames: EnumerateNames[Shape] = EnumerateNames.derived[Shape]
}

@joroKr21
Copy link
Member

joroKr21 commented Jun 7, 2021

Same problem with shapeless-3

bishabosha added a commit to dotty-staging/dotty that referenced this issue Sep 3, 2021
add a new method healPrefix which converts
ThisTypes wrapping a Module into a TermRef
and subsitutes ThisType of enclosing classes
with matching parts of the prefix of the
summoned mirror

fixes scala#12328
fixes scala#11174
bishabosha added a commit to dotty-staging/dotty that referenced this issue Mar 2, 2022
add a new method healPrefix which converts
ThisTypes wrapping a Module into a TermRef
and subsitutes ThisType of enclosing classes
with matching parts of the prefix of the
summoned mirror

fixes scala#12328
fixes scala#11174
bishabosha added a commit to dotty-staging/dotty that referenced this issue Apr 1, 2022
add a new method healPrefix which converts
ThisTypes wrapping a Module into a TermRef
and subsitutes ThisType of enclosing classes
with matching parts of the prefix of the
summoned mirror

fixes scala#12328
fixes scala#11174
bishabosha added a commit to dotty-staging/dotty that referenced this issue Apr 1, 2022
add a new method healPrefix which converts
ThisTypes wrapping a Module into a TermRef
and subsitutes ThisType of enclosing classes
with matching parts of the prefix of the
summoned mirror

fixes scala#12328
fixes scala#11174
bishabosha added a commit to dotty-staging/dotty that referenced this issue May 16, 2022
add a new method healPrefix which converts
ThisTypes wrapping a Module into a TermRef
and subsitutes ThisType of enclosing classes
with matching parts of the prefix of the
summoned mirror

fixes scala#12328
fixes scala#11174
bishabosha added a commit to dotty-staging/dotty that referenced this issue May 16, 2022
add a new method healPrefix which converts
ThisTypes wrapping a Module into a TermRef
and subsitutes ThisType of enclosing classes
with matching parts of the prefix of the
summoned mirror

fixes scala#12328
fixes scala#11174
bishabosha added a commit to dotty-staging/dotty that referenced this issue May 18, 2022
add a new method healPrefix which converts
ThisTypes wrapping a Module into a TermRef
and subsitutes ThisType of enclosing classes
with matching parts of the prefix of the
summoned mirror

fixes scala#12328
fixes scala#11174

use do not force symbols

add safety for unknown type

experiment with TypeOps.refineUsingParent

support hk types

rebase fixes

disable for scala2x generic product nonstatic

simplify a bit

add more tests

find common prefix of and/or types

refine implementation based on runtime tests

experiment with supertypes

remove prefix splice in companionref
@sirthias
Copy link
Contributor

Just tripped across this issue as well in the Scala 3 upgrade of the borer macros...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment