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

-Xcheck-macros fails in pismute/classy-optics #18353

Closed
WojciechMazur opened this issue Aug 7, 2023 · 4 comments · Fixed by #18354
Closed

-Xcheck-macros fails in pismute/classy-optics #18353

WojciechMazur opened this issue Aug 7, 2023 · 4 comments · Fixed by #18354
Assignees
Labels

Comments

@WojciechMazur
Copy link
Contributor

Failure found in OpenCB for pismute/classy-optics - build logs

I'm not sure, but it looks like a false-positive. Can we confirm that?

Compiler version

Since 3.3.0

Minimized code

// macros.scala
import scala.compiletime.*
import scala.deriving.*
import scala.quoted.*

trait Getter[S, A]:
  def view: S => A

trait Lens[S, A] extends Getter[S, A]:
  def set: S => A => S

object Lens {
  inline def apply[S, A](_view: S => A)(_set: S => A => S): Lens[S, A] =
    new Lens[S, A]:
      def view: S => A = _view
      def set: S => A => S = _set

  inline given derived[T <: Product, A]: Lens[T, A] = ${
    ProductMacros.genLens[T, A]
  }
}

object ProductMacros {
  private def indexOf[T: Type, A: Type](using Quotes): Int =
    indexOf0[T, A](0)

  private def indexOf0[T: Type, A: Type](acc: Int)(using Quotes): Int =
    Type.of[T] match
      case '[EmptyTuple]  => -1
      case '[A *: tpes]   => acc
      case '[tpe *: tpes] => indexOf0[tpes, A](acc + 1)

  def genLens[T <: Product: Type, A: Type](using
      q: Quotes
  ): Expr[Lens[T, A]] = {
    import quotes.reflect.*

    Expr
      .summon[Mirror.ProductOf[T]]
      .map {
        case '{
              $m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes }
            } =>
          val i = indexOf[elementTypes, A]
          if i < 0 then
            report.errorAndAbort(s"has no the field of ${Type.show[A]}")
          else
            val ii: Expr[Int] = Expr(i)
            val view: Expr[T => A] = '{ t =>
              t.productElement($ii).asInstanceOf[A]
            }
            val set: Expr[T => A => T] = '{ t => a =>
              val arr = Tuple.fromProduct(t).toArray
              arr($ii) = a.asInstanceOf[Object]
              // Check-macros fails here probably
              $m.fromTuple(Tuple.fromArray(arr).asInstanceOf[elementTypes])
            }
            '{ Lens[T, A]($view)($set) }
      }
      .getOrElse(
        report.errorAndAbort(s"${Type.show[T]} is not a product type")
      )

  }
}
// main.scala
//> using options -Xcheck-macros

@main def Test = {
  
  type TupleConfig = (Int, String)

  val tConfig = (1, "string")
  val fails = summon[Lens[TupleConfig, Int]].view(tConfig)
}

Output

scala-cli run  --server=false macros.scala main.scala -S 3.3.0    
Warning: setting /Users/wmazur/projects/sandbox as the project root directory for this run.
-- Error: /Users/wmazur/projects/sandbox/main.scala:8:44 -----------------------
 8 |  val fails = summon[Lens[TupleConfig, Int]].view(tConfig)
   |                                            ^
   |Malformed tree was found while expanding macro with -Xcheck-macros.
   |               |The tree does not conform to the compiler's tree invariants.
   |               |
   |               |Macro was:
   |               |scala.quoted.runtime.Expr.splice[Lens[scala.Tuple2[scala.Int, scala.Predef.String], scala.Int]](((evidence$1: scala.quoted.Quotes) ?=> ProductMacros.genLens[scala.Tuple2[scala.Int, scala.Predef.String], scala.Int](scala.quoted.Type.of[scala.Tuple2[scala.Int, scala.Predef.String]](evidence$1), scala.quoted.Type.of[scala.Int](evidence$1), evidence$1)))
   |               |
   |               |The macro returned:
   |               |Lens.apply[scala.Tuple2[scala.Int, scala.Predef.String], scala.Int](((t: scala.Tuple2[scala.Int, scala.Predef.String]) => t.productElement(0).asInstanceOf[scala.Int]))(((`t₂`: scala.Tuple2[scala.Int, scala.Predef.String]) => ((a: scala.Int) => {
   |  val arr: scala.Array[java.lang.Object] = scala.Tuple.fromProduct(`t₂`).toArray
   |  arr.update(0, a.asInstanceOf[java.lang.Object])
   |  scala.deriving.Mirror.fromTuple[scala.Tuple2[scala.Int, scala.Predef.String]](new scala.runtime.TupleMirror(2).$asInstanceOf$[scala.deriving.Mirror.Product {
   |    type MirroredMonoType >: scala.Tuple2[scala.Int, scala.Predef.String] <: scala.Tuple2[scala.Int, scala.Predef.String]
   |    type MirroredType >: scala.Tuple2[scala.Int, scala.Predef.String] <: scala.Tuple2[scala.Int, scala.Predef.String]
   |    type MirroredLabel >: "Tuple2" <: "Tuple2"
   |    type MirroredElemTypes >: scala.*:[scala.Int, scala.*:[scala.Predef.String, scala.Tuple$package.EmptyTuple]] <: scala.*:[scala.Int, scala.*:[scala.Predef.String, scala.Tuple$package.EmptyTuple]]
   |    type MirroredElemLabels >: scala.*:["_1", scala.*:["_2", scala.Tuple$package.EmptyTuple]] <: scala.*:["_1", scala.*:["_2", scala.Tuple$package.EmptyTuple]]
   |  }])(scala.Tuple.fromArray[java.lang.Object](arr).asInstanceOf[scala.*:[scala.Int, scala.*:[scala.Predef.String, scala.Tuple$package.EmptyTuple]]])
   |})))
   |               |
   |               |Error:
   |               |assertion failed: position not set for 
   |  scala.deriving.Mirror.Product{
   |    type MirroredMonoType = (Int, String); type MirroredType = (Int, String);
   |      type MirroredLabel = ("Tuple2" : String);
   |      type MirroredElemTypes = (Int, String);
   |      type MirroredElemLabels = (("_1" : String), ("_2" : String))
   |  }
   | # -1 of class dotty.tools.dotc.ast.Trees$TypeTree in /Users/wmazur/projects/sandbox/main.scala
   |               |
   |               |
   |----------------------------------------------------------------------------
   |Inline stack trace
   |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   |This location contains code that was inlined from macros.scala:17
17 |  inline given derived[T <: Product, A]: Lens[T, A] = ${
   |                                                      ^
18 |    ProductMacros.genLens[T, A]
19 |  }
    ----------------------------------------------------------------------------
1 error found

Expectation

Is it a false-positive? It does not look like one

@nicolasstucki
Copy link
Contributor

It also fails with -Ycheck:inlining.

@nicolasstucki
Copy link
Contributor

nicolasstucki commented Aug 8, 2023

How I fixed this

First, I reproduced the issue with scalac macro.sclaa main.scala -Yshow-tree-ids to show the id of the tree without a span. In my case I got:

   |assertion failed: position not set for 
   |  scala.deriving.Mirror.Product{
   |    type MirroredMonoType = (Int, String); type MirroredType = (Int, String);
   |      type MirroredLabel = ("Tuple2" : String);
   |      type MirroredElemTypes = (Int, String);
   |      type MirroredElemLabels = (("_1" : String), ("_2" : String))
   |  }
   |#22863 # 22863 of class dotty.tools.dotc.ast.Trees$TypeTree in main.scala

The tree with no span had the id 22863. Therefore I used scalac macro.sclaa main.scala -Ydebug-tree-with-id 22863 to print the stack where that tree was created.

Debug tree (id=22863) creation 
TypeTree

java.lang.Exception: Stack trace
        at java.base/java.lang.Thread.dumpStack(Thread.java:1380)
        at dotty.tools.dotc.ast.Positioned.allocateId(Positioned.scala:38)
        at dotty.tools.dotc.ast.Positioned.<init>(Positioned.scala:40)
        at dotty.tools.dotc.ast.Trees$Tree.<init>(Trees.scala:51)
        at dotty.tools.dotc.ast.Trees$DenotingTree.<init>(Trees.scala:253)
        at dotty.tools.dotc.ast.Trees$TypeTree.<init>(Trees.scala:772)
        at dotty.tools.dotc.ast.untpd$.TypeTree(untpd.scala:413)
        at dotty.tools.dotc.ast.tpd$.TypeTree(tpd.scala:186)
        at dotty.tools.dotc.ast.tpd$TreeOps$.cast$extension(tpd.scala:1024)
        at dotty.tools.dotc.typer.Synthesizer.makeProductMirror$1(Synthesizer.scala:434)
        at dotty.tools.dotc.typer.Synthesizer.productMirror(Synthesizer.scala:470)
        at dotty.tools.dotc.typer.Synthesizer.$init$$$anonfun$6$$anonfun$1$$anonfun$1$$anonfun$1(Synthesizer.scala:601)
       ...

at dotty.tools.dotc.typer.Synthesizer.makeProductMirror$1(Synthesizer.scala:434) I noticed that we have

withNoErrors(mirrorRef.cast(mirrorType))

where mirrorRef had a span, but the casted version did not. And therefore added the missing span explicitly and fixed the issue.

withNoErrors(mirrorRef.cast(mirrorType).withSpan(span))

@WojciechMazur
Copy link
Contributor Author

After fixing this issue we should check if it also resolves #17750 Otherwise I'll try to minimize it as well

@nicolasstucki
Copy link
Contributor

The error in #17750 is unrelated to this one.

WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 19, 2024
WojciechMazur pushed a commit that referenced this issue Jun 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants