Skip to content

Commit

Permalink
Patch underlyingArgument to avoid mapping into modules (#18923)
Browse files Browse the repository at this point in the history
Do not look under RHS of the `lazy val` of an object.

Fixes #18911
  • Loading branch information
odersky authored Nov 28, 2023
2 parents ab4f0e2 + 9bfeb1c commit 07ff4fb
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 1 deletion.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
*/
private class MapToUnderlying extends TreeMap {
override def transform(tree: Tree)(using Context): Tree = tree match {
case tree: Ident if isBinding(tree.symbol) && skipLocal(tree.symbol) =>
case tree: Ident if isBinding(tree.symbol) && skipLocal(tree.symbol) && !tree.symbol.is(Module) =>
tree.symbol.defTree match {
case defTree: ValOrDefDef =>
val rhs = defTree.rhs
Expand Down
91 changes: 91 additions & 0 deletions tests/pos-macros/i18911/Macros_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import scala.quoted._
import scala.compiletime.testing.{typeChecks, typeCheckErrors}

trait Assertion
trait Bool {
def value: Boolean
}
class SimpleMacroBool(expression: Boolean) extends Bool {
override def value: Boolean = expression
}
class BinaryMacroBool(left: Any, operator: String, right: Any, expression: Boolean) extends Bool {
override def value: Boolean = expression
}
object Bool {
def simpleMacroBool(expression: Boolean): Bool = new SimpleMacroBool(expression)
def binaryMacroBool(left: Any, operator: String, right: Any, expression: Boolean): Bool =
new BinaryMacroBool(left, operator, right, expression)
def binaryMacroBool(left: Any, operator: String, right: Any, bool: Bool): Bool =
new BinaryMacroBool(left, operator, right, bool.value)
}

object Assertions {
inline def assert(inline condition: Boolean): Assertion =
${ AssertionsMacro.assert('{ condition }) }
}

object AssertionsMacro {
def assert(condition: Expr[Boolean])(using Quotes): Expr[Assertion] =
transform(condition)

def transform(
condition: Expr[Boolean]
)(using Quotes): Expr[Assertion] = {
val bool = BooleanMacro.parse(condition)
'{
new Assertion {
val condition = $bool
}
}
}
}

object BooleanMacro {
private val supportedBinaryOperations =
Set("!=", "==")

def parse(condition: Expr[Boolean])(using Quotes): Expr[Bool] = {
import quotes.reflect._
import quotes.reflect.ValDef.let
import util._

def exprStr: String = condition.show
def defaultCase = '{ Bool.simpleMacroBool($condition) }

def isByNameMethodType(tp: TypeRepr): Boolean = tp.widen match {
case MethodType(_, ByNameType(_) :: Nil, _) => true
case _ => false
}

condition.asTerm.underlyingArgument match { // WARNING: unsound use of `underlyingArgument`
case Apply(sel @ Select(lhs, op), rhs :: Nil) =>
def binaryDefault =
if (isByNameMethodType(sel.tpe)) defaultCase
else if (supportedBinaryOperations.contains(op)) {
let(Symbol.spliceOwner, lhs) { left =>
let(Symbol.spliceOwner, rhs) { right =>
val app = left.select(sel.symbol).appliedTo(right)
let(Symbol.spliceOwner, app) { result =>
val l = left.asExpr
val r = right.asExpr
val b = result.asExprOf[Boolean]
val code = '{ Bool.binaryMacroBool($l, ${ Expr(op) }, $r, $b) }
code.asTerm
}
}
}.asExprOf[Bool]
} else defaultCase

op match {
case "==" => binaryDefault
case _ => binaryDefault
}

case Literal(_) =>
'{ Bool.simpleMacroBool($condition) }

case _ =>
defaultCase
}
}
}
5 changes: 5 additions & 0 deletions tests/pos-macros/i18911/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@main def Test = {
case class Document()
val expected: Document = ???
Assertions.assert( expected == Document()) // error
}

0 comments on commit 07ff4fb

Please sign in to comment.