From b335873493c5935405e3bf90fdbd9ddc0a4790a0 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 21 Jun 2021 17:59:55 +0200 Subject: [PATCH] Properly handle self-types in reflection member lookup The type on which we lookup things needs to be the this-type of the class, otherwise it will not include members that only appear in the self-type, since these members can appear in the types of members of the current class, this could lead to a MissingType exception being thrown. Fixes #12081. --- .../quoted/runtime/impl/QuotesImpl.scala | 15 +++++--- .../scaladoc/tasty/SyntheticSupport.scala | 8 ++++- tests/run-macros/self.check | 3 ++ tests/run-macros/self/Macro_1.scala | 35 +++++++++++++++++++ tests/run-macros/self/Test_2.scala | 8 +++++ 5 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 tests/run-macros/self.check create mode 100644 tests/run-macros/self/Macro_1.scala create mode 100644 tests/run-macros/self/Test_2.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 83fc002217f3..9e6669fe9256 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2483,15 +2483,22 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def declaredFields: List[Symbol] = self.unforcedDecls.filter(isField) + /** The prefix on which a member lookup should be performed. */ + private def lookupPrefix: TypeRepr = + if self.isClass then + self.thisType // Needed to handle self-types (as in tests/run-macros/self) + else + self.namedType + def memberField(name: String): Symbol = fieldMember(name) def fieldMember(name: String): Symbol = - appliedTypeRef(self).allMembers.iterator.map(_.symbol).find { + lookupPrefix.allMembers.iterator.map(_.symbol).find { sym => isField(sym) && sym.name.toString == name }.getOrElse(dotc.core.Symbols.NoSymbol) def memberFields: List[Symbol] = fieldMembers def fieldMembers: List[Symbol] = - appliedTypeRef(self).allMembers.iterator.map(_.symbol).collect { + lookupPrefix.allMembers.iterator.map(_.symbol).collect { case sym if isField(sym) => sym.asTerm }.toList @@ -2507,13 +2514,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def memberMethod(name: String): List[Symbol] = methodMember(name) def methodMember(name: String): List[Symbol] = - appliedTypeRef(self).allMembers.iterator.map(_.symbol).collect { + lookupPrefix.allMembers.iterator.map(_.symbol).collect { case sym if isMethod(sym) && sym.name.toString == name => sym.asTerm }.toList def memberMethods: List[Symbol] = methodMembers def methodMembers: List[Symbol] = - appliedTypeRef(self).allMembers.iterator.map(_.symbol).collect { + lookupPrefix.allMembers.iterator.map(_.symbol).collect { case sym if isMethod(sym) => sym.asTerm }.toList diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala index efc7909dbe96..d1fc5ed72a32 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SyntheticSupport.scala @@ -74,7 +74,13 @@ object SyntheticsSupport: import dotty.tools.dotc given ctx: dotc.core.Contexts.Context = quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx val sym = rsym.asInstanceOf[dotc.core.Symbols.Symbol] - sym.typeRef.appliedTo(sym.typeParams.map(_.typeRef)).allMembers.iterator.map(_.symbol) + // `lookupPrefix` is private in `QuotesImpl#SymbolMethods` + val lookupPrefix = + if sym.isClass then + sym.thisType + else + sym.namedType + lookupPrefix.allMembers.iterator.map(_.symbol) .collect { case sym if (!sym.is(dotc.core.Flags.ModuleVal) || sym.is(dotc.core.Flags.Given)) && diff --git a/tests/run-macros/self.check b/tests/run-macros/self.check new file mode 100644 index 000000000000..925316ca5318 --- /dev/null +++ b/tests/run-macros/self.check @@ -0,0 +1,3 @@ +ExprType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),Int)) +MethodType(List(x), List(TypeRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),Predef),String)), TypeRef(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class )),module class )),B),X)) +MethodType(List(x), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),Int)), TypeRef(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class )),module class )),B),X)) diff --git a/tests/run-macros/self/Macro_1.scala b/tests/run-macros/self/Macro_1.scala new file mode 100644 index 000000000000..98699f2ece14 --- /dev/null +++ b/tests/run-macros/self/Macro_1.scala @@ -0,0 +1,35 @@ +import scala.quoted.* + +trait A { + type X +} +trait B { self: A => + def foo(x: Int): X + def foo(x: String): X +} + +object Obj { + def foo: Int = 1 +} + +object Macros { + + inline def test(): String = ${ testImpl } + + private def testImpl(using Quotes) : Expr[String] = { + import quotes.reflect.* + val bTpe = TypeRepr.of[B] + val bSym = bTpe.classSymbol.get + val bMethSyms = bSym.methodMember("foo") // Used to throw a MissingType exception + val bMethTpes = bMethSyms.map(bTpe.memberType) + + // Make sure we didn't break member lookup on terms + val objTpe = TypeRepr.of[Obj.type] + val objSym = objTpe.termSymbol + val objMethSyms = objSym.methodMember("foo") + val objMethTpes = objMethSyms.map(objTpe.memberType) + + Expr((objMethTpes ++ bMethTpes).map(_.toString).sorted.mkString("\n")) + } + +} diff --git a/tests/run-macros/self/Test_2.scala b/tests/run-macros/self/Test_2.scala new file mode 100644 index 000000000000..f0a2a8a00ab8 --- /dev/null +++ b/tests/run-macros/self/Test_2.scala @@ -0,0 +1,8 @@ + +object Test { + + def main(args: Array[String]): Unit = { + println(Macros.test()) + } + +}