Skip to content
Open
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
31 changes: 29 additions & 2 deletions compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1880,15 +1880,42 @@ 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 =
// This check only fails if no owner of the passed member symbol is related to the `self` type.
// Ideally we would just check if the symbol is a member of
// the type (with self.derivesFrom(member.owner)), but:
// A) the children of enums are not members of those enums, which is unintuitive
// B) this check was added late, risking major hard to fix regressions
// (and failed dotty compilation tests)
// Additionally, it can be useful to be able to learn a type of a symbol using some indirect prefix,
// even if that symbol is not a direct member of that prefix, but a nested one.
def isTypeRelatedToThePassedMember =
import scala.util.boundary
boundary {
var checked: Symbol = member
while(checked.exists) {
if self.derivesFrom(checked)
|| self.typeSymbol.declarations.contains(checked)
|| self.typeSymbol.companionClass.declarations.contains(checked) then
boundary.break(true)
checked = checked.owner
}
boundary.break(false)
}
xCheckMacroAssert(isTypeRelatedToThePassedMember, s"$member is not a member of ${self.show}")

// 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)

memberInfo.asSeenFrom(self, member.owner) match
case dotc.core.Types.ClassInfo(prefix, sym, _, _, _) =>
// We do not want to expose ClassInfo in the reflect API, instead we change it to a TypeRef,
// see issue #22395
prefix.select(sym)
case other => other
def baseClasses: List[Symbol] = self.baseClasses
def baseType(cls: Symbol): TypeRepr = self.baseType(cls)
def derivesFrom(cls: Symbol): Boolean = self.derivesFrom(cls)
Expand Down
16 changes: 16 additions & 0 deletions tests/neg-macros/i15159.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- Error: tests/neg-macros/i15159/Test_2.scala:5:16 --------------------------------------------------------------------
5 | TestMacro.test[A] // error
| ^^^^^^^^^^^^^^^^^
| Exception occurred while executing macro expansion.
| java.lang.AssertionError: class X is not a member of A
| at TestMacro$.testImpl$$anonfun$1(Macro_1.scala:8)
| at scala.collection.immutable.List.map(List.scala:240)
| at TestMacro$.testImpl(Macro_1.scala:7)
|
|---------------------------------------------------------------------------------------------------------------------
|Inline stack trace
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|This location contains code that was inlined from Macro_1.scala:3
3 | inline def test[T]: Unit = ${ testImpl[T] }
| ^^^^^^^^^^^^^^^^
---------------------------------------------------------------------------------------------------------------------
10 changes: 10 additions & 0 deletions tests/neg-macros/i15159/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import scala.quoted.*
object TestMacro:
inline def test[T]: Unit = ${ testImpl[T] }
def testImpl[T: Type](using Quotes): Expr[Unit] =
import quotes.reflect.*
val tpe = TypeRepr.of[T]
tpe.typeSymbol.children.map { childSymbol =>
tpe.memberType(childSymbol) // not a member of tpe
}
'{ () }
6 changes: 6 additions & 0 deletions tests/neg-macros/i15159/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
sealed trait A
case class X(i: Int) extends A

object Test extends App {
TestMacro.test[A] // error
}
12 changes: 12 additions & 0 deletions tests/pos-macros/i13319/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import scala.quoted._
object Macro:
inline def apply[A]: Unit = ${impl[A]}

private def impl[A: Type](using Quotes): Expr[String] =
import quotes.reflect._
val t = TypeRepr.of[A]
Expr.ofList(t.baseClasses.drop(1).filter(_.flags.is(Flags.Trait)).map { baseSymbol =>
t.memberType(baseSymbol).asType match { case '[t] => 42}
Expr("")
})
Expr("")
1 change: 1 addition & 0 deletions tests/pos-macros/i13319/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@main def Test = Macro[Option[String]]
1 change: 1 addition & 0 deletions tests/run-macros/i22395.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TypeRef(AppliedType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class <root>)),module class <empty>)),Foo),List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class <root>)),object scala),Int))),class Nested)
14 changes: 14 additions & 0 deletions tests/run-macros/i22395/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import scala.quoted._

inline def test(): String = ${testImpl}
def testImpl(using Quotes) = {
import quotes.reflect._
val fooSymbol = TypeRepr.of[Foo[Int]].typeSymbol
val nestedSymbol = fooSymbol.typeMember("Nested")

Expr(TypeRepr.of[Foo[Int]].memberType(nestedSymbol).toString)
}


trait Foo[X]:
sealed abstract class Nested extends Foo[Int]
2 changes: 2 additions & 0 deletions tests/run-macros/i22395/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@main def Test =
println(test())
Loading