Skip to content

Commit

Permalink
Try not to approximate prefixes when using memberType in reflect API (#…
Browse files Browse the repository at this point in the history
…22448)

To achieve this, we substitute This types in `member.info` referencing classSymbol with TypeReprs actual self (which may or may not be a This type)
Fixes #22424
  • Loading branch information
jchyb authored Mar 4, 2025
1 parent 8601228 commit 2685750
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 2 deletions.
12 changes: 10 additions & 2 deletions compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1694,7 +1694,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
end SimpleSelectorTypeTest

object SimpleSelector extends SimpleSelectorModule:
def apply(name: String): SimpleSelector =
def apply(name: String): SimpleSelector =
withDefaultPos(untpd.ImportSelector(untpd.Ident(name.toTermName)))
def unapply(x: SimpleSelector): Some[String] = Some(x.name.toString)
end SimpleSelector
Expand Down Expand Up @@ -1837,7 +1837,15 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
def termSymbol: Symbol = self.termSymbol
def isSingleton: Boolean = self.isSingleton
def memberType(member: Symbol): TypeRepr =
member.info.asSeenFrom(self, member.owner)
// we replace thisTypes here to avoid resolving otherwise unstable prefixes into Nothing
val memberInfo =
if self.typeSymbol.isClassDef then
member.info.substThis(self.classSymbol.asClass, self)
else
member.info
memberInfo
.asSeenFrom(self, member.owner)

def baseClasses: List[Symbol] = self.baseClasses
def baseType(cls: Symbol): TypeRepr = self.baseType(cls)
def derivesFrom(cls: Symbol): Boolean = self.derivesFrom(cls)
Expand Down
25 changes: 25 additions & 0 deletions tests/pos-macros/i22424/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

import scala.quoted.*

object MockMaker:
inline def inlineMock[T]: Unit = ${instance[T]}
transparent inline def transparentInlineMock[T]: Unit = ${instance[T]}

def instance[T: Type](using quotes: Quotes): Expr[Unit] =
import quotes.reflect._
val tpe = TypeRepr.of[T]
val symbol = tpe.typeSymbol.methodMember("innerTraitInOptions").head
tpe.memberType(symbol) match
case mt @ MethodType(_, args, _) =>
assert(args.head.typeSymbol != TypeRepr.of[Nothing].typeSymbol, "argument is incorrectly approximated")
val shownType = mt.show
val expectedType = "(x: m.Embedded#ATrait[scala.Predef.String, scala.Int])m.Embedded#ATrait[scala.Predef.String, scala.Int]"
assert(shownType == expectedType, s"Incorrect type shown. Obtained: $shownType, Expected: $expectedType")
'{()}

trait PolymorphicTrait {
trait Embedded {
trait ATrait[A, B]
def innerTraitInOptions(x: ATrait[String, Int]): ATrait[String, Int]
}
}
4 changes: 4 additions & 0 deletions tests/pos-macros/i22424/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@main def Test =
val m = new PolymorphicTrait {}
MockMaker.inlineMock[m.Embedded]
MockMaker.transparentInlineMock[m.Embedded]

0 comments on commit 2685750

Please sign in to comment.