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

Splice accessing path-dependent type with parameter crashes the compiler #16615

Closed
jodersky opened this issue Jan 4, 2023 · 2 comments · Fixed by #16773
Closed

Splice accessing path-dependent type with parameter crashes the compiler #16615

jodersky opened this issue Jan 4, 2023 · 2 comments · Fixed by #16773
Assignees
Milestone

Comments

@jodersky
Copy link
Contributor

jodersky commented Jan 4, 2023

Compiler version

master branch 3.3.0-RC1-bin-SNAPSHOT-nonbootstrapped-git-263eada

and also on latest official release, 3.2.1

Minimized code

class GlobalReader[E]
trait Api:
  type Reader[E]

object Macros:
  import quoted.Expr
  import quoted.Quotes
  import quoted.Type

  def get[T](using Type[T]) = ??? // this is a placeholder, it could be Expr.summon for example

  def bugImpl[A <: Api: Type](using qctx: Quotes)(api: Expr[A]) =
    import qctx.reflect.*

    val e = TypeRepr.of[Int] // e is constructed using the reflect api directly

    e.asType match
      case '[t] =>
        '{
          type GR = GlobalReader[t]

          val p = $api
          type PR = p.Reader[t]

          ${
            // ok
            get[GR]

            // crash
            // java.lang.AssertionError: assertion failed: unresolved symbols: type t$given1$_$3 (line 32) #18110 when pickling /home/jodersky/p/bug/macro2.scala while running pickleQuotes on /home/jodersky/p/bug/macro2.scala
            get[PR]

            // workaround
            get(using Type.of[PR])
          }
        }

Output (click arrow to expand)

Compile with -Xcheck-macros -Ycheck:all

checking /home/jodersky/p/bug/macro2.scala after phase typer
checking /home/jodersky/p/bug/macro2.scala after phase inlinedPositions
checking /home/jodersky/p/bug/macro2.scala after phase posttyper
checking /home/jodersky/p/bug/macro2.scala after phase pickler
checking /home/jodersky/p/bug/macro2.scala after phase inlining
checking /home/jodersky/p/bug/macro2.scala after phase postInlining
checking /home/jodersky/p/bug/macro2.scala after phase staging
checking /home/jodersky/p/bug/macro2.scala after phase splicing
java.lang.AssertionError: assertion failed: unresolved symbols: type t$given1$_$3 (line 32) #18110 when pickling /home/jodersky/p/bug/macro2.scala while running pickleQuotes on /home/jodersky/p/bug/macro2.scala
exception occurred while compiling /home/jodersky/p/bug/macro2.scala
Exception in thread "main" java.lang.AssertionError: assertion failed: unresolved symbols: type t$given1$_$3 (line 32) #18110 when pickling /home/jodersky/p/bug/macro2.scala while compiling /home/jodersky/p/bug/macro2.scala
java.lang.AssertionError: assertion failed: unresolved symbols: type t$given1$_$3 (line 32) #18110 when pickling /home/jodersky/p/bug/macro2.scala
	at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
	at dotty.tools.dotc.core.tasty.TreePickler.pickle(TreePickler.scala:812)
	at dotty.tools.dotc.quoted.PickledQuotes$.pickle(PickledQuotes.scala:221)
	at dotty.tools.dotc.quoted.PickledQuotes$.pickleQuote(PickledQuotes.scala:33)
	at dotty.tools.dotc.transform.PickleQuotes$.pickleAsTasty$1(PickleQuotes.scala:292)
	at dotty.tools.dotc.transform.PickleQuotes$.apply(PickleQuotes.scala:378)
	at dotty.tools.dotc.transform.PickleQuotes$$anon$1.transform(PickleQuotes.scala:106)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformBlock$$anonfun$1$$anonfun$1(tpd.scala:1230)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.loop$2(tpd.scala:1212)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1225)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformBlock(tpd.scala:1230)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1424)
	at dotty.tools.dotc.transform.MacroTransform$Transformer.transform(MacroTransform.scala:49)
	at dotty.tools.dotc.transform.PickleQuotes$$anon$1.transform(PickleQuotes.scala:121)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1432)
	at dotty.tools.dotc.transform.MacroTransform$Transformer.transform(MacroTransform.scala:49)
	at dotty.tools.dotc.transform.PickleQuotes$$anon$1.transform(PickleQuotes.scala:121)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform$$anonfun$1(Trees.scala:1505)
	at scala.collection.immutable.List.mapConserve(List.scala:472)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1505)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transformSub(Trees.scala:1509)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1430)
	at dotty.tools.dotc.transform.MacroTransform$Transformer.transform(MacroTransform.scala:49)
	at dotty.tools.dotc.transform.PickleQuotes$$anon$1.transform(PickleQuotes.scala:121)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformBlock$$anonfun$1$$anonfun$1(tpd.scala:1230)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.loop$2(tpd.scala:1212)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1225)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformBlock(tpd.scala:1230)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1424)
	at dotty.tools.dotc.transform.MacroTransform$Transformer.transform(MacroTransform.scala:49)
	at dotty.tools.dotc.transform.PickleQuotes$$anon$1.transform(PickleQuotes.scala:121)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1476)
	at dotty.tools.dotc.transform.MacroTransform$Transformer.transform(MacroTransform.scala:40)
	at dotty.tools.dotc.transform.PickleQuotes$$anon$1.transform(PickleQuotes.scala:121)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.loop$2(tpd.scala:1225)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1225)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1227)
	at dotty.tools.dotc.transform.MacroTransform$Transformer.transform(MacroTransform.scala:47)
	at dotty.tools.dotc.transform.PickleQuotes$$anon$1.transform(PickleQuotes.scala:121)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1478)
	at dotty.tools.dotc.transform.MacroTransform$Transformer.transform(MacroTransform.scala:40)
	at dotty.tools.dotc.transform.PickleQuotes$$anon$1.transform(PickleQuotes.scala:121)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.loop$2(tpd.scala:1225)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1225)
	at dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1227)
	at dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1486)
	at dotty.tools.dotc.transform.MacroTransform$Transformer.transform(MacroTransform.scala:40)
	at dotty.tools.dotc.transform.PickleQuotes$$anon$1.transform(PickleQuotes.scala:121)
	at dotty.tools.dotc.transform.MacroTransform.run(MacroTransform.scala:18)
	at dotty.tools.dotc.transform.PickleQuotes.run(PickleQuotes.scala:96)
	at dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:324)
	at scala.collection.immutable.List.map(List.scala:246)
	at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:328)
	at dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:244)
	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:1321)
	at dotty.tools.dotc.Run.runPhases$1(Run.scala:260)
	at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:268)
	at dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:277)
	at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:68)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:277)
	at dotty.tools.dotc.Run.compileSources(Run.scala:195)
	at dotty.tools.dotc.Run.compile(Run.scala:179)
	at dotty.tools.dotc.Driver.doCompile(Driver.scala:35)
	at dotty.tools.dotc.Driver.process(Driver.scala:195)
	at dotty.tools.dotc.Driver.process(Driver.scala:163)
	at dotty.tools.dotc.Driver.process(Driver.scala:175)
	at dotty.tools.dotc.Driver.main(Driver.scala:205)
	at dotty.tools.dotc.Main.main(Main.scala)

Expectation

I would think that both path-dependent and non-dependent types can be accessed the same way within splices.

@jodersky jodersky added itype:bug itype:crash stat:needs triage Every issue needs to have an "area" and "itype" label labels Jan 4, 2023
@prolativ prolativ added area:metaprogramming:quotes Issues related to quotes and splices area:pickling and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jan 5, 2023
@nicolasstucki
Copy link
Contributor

Minimized

import scala.quoted.*

trait Api:
  type Reader[E]

def bugImpl[T: Type](using Quotes) =
  '{
    val p: Api = ???
    ${
      Type.of[p.Reader[T]]
      Expr(1)
    }
  }

@nicolasstucki
Copy link
Contributor

After the splicing phase we get

 def bugImpl[T >: Nothing <: Any](implicit evidence$1: quoted.Type[T],
      x$1: quoted.Quotes): quoted.Expr[Int] =
      '{
        {
          val p: Api = ???
          {{{ 0 | Int | p.Reader[evidence$1$_$1] |
            {
              def $anonfun(Type$1: scala.quoted.Type[p.Reader[evidence$1$_$1]])
                : (quoted.Quotes) ?=> quoted.Expr[Int] =
                {
                  def $anonfun(using evidence$2: quoted.Quotes):
                    quoted.Expr[Int] =
                    {
                      Type$1
                      quoted.Expr.apply[Int](1)(quoted.ToExpr.IntToExpr[Int])(
                        evidence$2)
                    }
                  closure($anonfun)
                }
              closure($anonfun:
                scala.quoted.Type[p.Reader[evidence$1$_$1]] => (quoted.Quotes)
                  ?=> quoted.Expr[Int]
              )
            }
           }}}
        }
      }.apply(x$1)

Having evidence$1$_$1 in {{{ 0 | Int | p.Reader[evidence$1$_$1] | is wrong.

Instead, we should probably have

 def bugImpl[T >: Nothing <: Any](implicit evidence$1: quoted.Type[T],
      x$1: quoted.Quotes): quoted.Expr[Int] =
      '{
        {
          val p: Api = ???
          {{{ 0 | Int | p.Reader |
            {
              def $anonfun(Type$1: scala.quoted.Type[p.Reader])
                : (quoted.Quotes) ?=> quoted.Expr[Int] =
                {
                  def $anonfun(using evidence$2: quoted.Quotes):
                    quoted.Expr[Int] =
                    {
                      quoted.Type.of[{
                        @SplicedType type evidence$1$_$1 = [[[ 0 | evidence$1.Underlying | | evidence$1 ]]]
                        @SplicedType type evidence$1$_$2 = [[[ 1 | Type$1.Underlying | | Type$1 ]]]
                         evidence$1$_$2[evidence$1$_$1]
                      }
                      quoted.Expr.apply[Int](1)(quoted.ToExpr.IntToExpr[Int])(
                        evidence$2)
                    }
                  closure($anonfun)
                }
              closure($anonfun:
                scala.quoted.Type[p.Reader] => (quoted.Quotes)
                  ?=> quoted.Expr[Int]
              )
            }
           }}}
        }
      }.apply(x$1)

@Kordyjan Kordyjan added this to the Future versions milestone Jan 13, 2023
@Kordyjan Kordyjan modified the milestones: Future versions, 3.3.1-RC1 Jan 16, 2023
nicolasstucki added a commit that referenced this issue Feb 9, 2023
Fixes #16615

Previously it was assumed that the type in Type.of could be captured as
a whole, which meant that path dependent types for which a separate
@SplicedType hole definitions were included in a block, would end up
with missing references.

Now when find a block in Type.of, we try to analise all parts of the
type separately, adding additional hole definitions to the block as
necessary.
For types that can be captured as a whole (those which did not have a
block generated previously, meaning they do not include any @SplicedType
hole definitions), old method is used.

In essence, ended up replicating the trees proposed in the original
issue thread, which were incredibly helpful.
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.

5 participants