Skip to content

Macro expansion exception related to Select #5786

@liufengyun

Description

@liufengyun

The following macro throws exception at expansion time. The code that is culprit is the 2nd case where we used Term.Select.copy.

The usage of the method Term.Select.copy is a walkaround because we lack proper constructors for select (#5567 ).

import scala.quoted._
import scala.tasty._

object scalatest {

  inline def assert(condition: => Boolean): Unit = ~assertImpl('(condition), '(""))

  def assertImpl(cond: Expr[Boolean], clue: Expr[Any])(implicit refl: Reflection): Expr[Unit] = {
    import refl._
    import util._
    import quoted.Toolbox.Default._

    def isImplicitMethodType(tp: Type): Boolean =
      Type.IsMethodType.unapply(tp).flatMap(tp => if tp.isImplicit then Some(true) else None).nonEmpty

    cond.unseal.underlyingArgument match {
      case Term.Apply(sel @ Term.Select(lhs, op), rhs :: Nil) =>
        val Term.IsSelect(select) = sel
        val cond = Term.Apply(Term.Select.copy(select)(lhs, ">"), rhs :: Nil).seal[Boolean]
        '{ scala.Predef.assert(~cond) }
      case Term.Apply(f @ Term.Apply(Term.IsSelect(sel @ Term.Select(Term.Apply(qual, lhs :: Nil), op)), rhs :: Nil), implicits)
      if isImplicitMethodType(f.tpe) =>
        let(lhs) { left =>
          let(rhs) { right =>
            let(Term.Apply(Term.Apply(Term.Select.copy(sel)(Term.Apply(qual, left :: Nil), op), right :: Nil), implicits)) { result =>
              val b = result.seal[Boolean]
              val code = '{ scala.Predef.assert(~b) }
              code.unseal
            }
          }
        }.seal[Unit]

      case _ =>
        '{ scala.Predef.assert(~cond) }
    }
  }

  inline def thisLineNumber = ~thisLineNumberImpl

  def thisLineNumberImpl(implicit refl: Reflection): Expr[Int] = {
    import refl._
    refl.rootPosition.startLine.toExpr
  }
}
object Test {
  import scalatest._

  trait EqInt
  implicit val eq: EqInt = new EqInt {}

  implicit class IntOps(x: Int) {
    def === (y: Int)(implicit c: EqInt) = x == y
  }

  def main(args: Array[String]): Unit = {
    val a: Int = 100
    assert(a === 5)
  }
}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions