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

Patch underlyingArgument to avoid mapping into modules #18923

Merged
merged 1 commit into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1262,7 +1262,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
}
Loading