Skip to content

Commit

Permalink
Merge pull request #359 from sjrd/member-not-found-fixes
Browse files Browse the repository at this point in the history
Fix #336: Fix all the member-not-founds in the dotty codebase.
  • Loading branch information
sjrd authored Oct 11, 2023
2 parents a6daa92 + f06a431 commit 71b58e4
Show file tree
Hide file tree
Showing 19 changed files with 422 additions and 57 deletions.
27 changes: 27 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,32 @@ final class Definitions private[tastyquery] (ctx: Context, rootPackage: PackageS

lazy val String_+ : TermSymbol = StringClass.findNonOverloadedDecl(nme.m_+)

private[tastyquery] def createEnumMagicMethods(cls: ClassSymbol): Unit =
createSpecialMethod(cls, nme.Constructor, PolyType(List(typeName("E")), List(NothingAnyBounds), UnitType))
end createEnumMagicMethods

/** Creates the members that are patched from stdLibPatches on Predef.
*
* dotc does that generically, but it does not really work for us, as we
* cannot read other files while loading one file.
*/
private[tastyquery] def createPredefMagicMethods(cls: ClassSymbol): Unit =
val nnMethodType = PolyType(List(typeName("T")))(
_ => List(NothingAnyBounds),
pt => MethodType(List(termName("x")))(_ => List(pt.paramRefs(0)), mt => AndType(mt.paramRefs(0), pt.paramRefs(0)))
)
createSpecialMethod(cls, termName("nn"), nnMethodType, Inline | Final | Extension)

val anyRefOrNull = OrType(AnyRefType, NullType)
val eqNeMethodType = MethodType(
List(termName("x")),
List(anyRefOrNull),
MethodType(List(termName("y")), List(anyRefOrNull), BooleanType)
)
createSpecialMethod(cls, termName("eq"), eqNeMethodType, Inline | Final | Extension)
createSpecialMethod(cls, termName("ne"), eqNeMethodType, Inline | Final | Extension)
end createPredefMagicMethods

private def createSpecialPolyClass(
name: TypeName,
paramFlags: FlagSet,
Expand Down Expand Up @@ -394,6 +420,7 @@ final class Definitions private[tastyquery] (ctx: Context, rootPackage: PackageS
lazy val ProductClass = scalaPackage.requiredClass("Product")

lazy val ErasedNothingClass = scalaRuntimePackage.requiredClass("Nothing$")
lazy val ErasedBoxedUnitClass = scalaRuntimePackage.requiredClass("BoxedUnit")

private[tastyquery] lazy val targetNameAnnotClass = scalaAnnotationPackage.optionalClass("targetName")

Expand Down
54 changes: 32 additions & 22 deletions tasty-query/shared/src/main/scala/tastyquery/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,24 @@ private[tastyquery] object Erasure:
erase(tpe, SourceLanguage.Scala3)

def erase(tpe: Type, language: SourceLanguage)(using Context): ErasedTypeRef =
erase(tpe, language, keepUnit = false)

def erase(tpe: Type, language: SourceLanguage, keepUnit: Boolean)(using Context): ErasedTypeRef =
given SourceLanguage = language
finishErase(preErase(tpe))
finishErase(preErase(tpe, keepUnit))
end erase

/** First pass of erasure, where some special types are preserved as is.
*
* In particular, `Any` is preserved as `Any`, instead of becoming
* `java.lang.Object`.
*/
private def preErase(tpe: Type)(using Context, SourceLanguage): ErasedTypeRef =
private def preErase(tpe: Type, keepUnit: Boolean)(using Context, SourceLanguage): ErasedTypeRef =
def hasArrayErasure(cls: ClassSymbol): Boolean =
cls == defn.ArrayClass || (cls == defn.RepeatedParamClass && summon[SourceLanguage] == SourceLanguage.Java)

def arrayOfBounds(bounds: TypeBounds): ErasedTypeRef =
preErase(bounds.high) match
preErase(bounds.high, keepUnit = false) match
case ClassRef(cls) if cls == defn.AnyClass || cls == defn.AnyValClass =>
ClassRef(defn.ObjectClass)
case typeRef =>
Expand All @@ -50,7 +53,8 @@ private[tastyquery] object Erasure:
case _ =>
arrayOf(tpe.translucentSuperType)
case TypeRef.OfClass(cls) =>
ClassRef(cls).arrayOf()
if cls == defn.UnitClass then ClassRef(defn.ErasedBoxedUnitClass).arrayOf()
else ClassRef(cls).arrayOf()
case tpe: TypeRef =>
tpe.optSymbol match
case Some(sym: TypeMemberSymbol) =>
Expand All @@ -61,7 +65,7 @@ private[tastyquery] object Erasure:
case _ =>
arrayOfBounds(tpe.bounds)
case tpe: TypeParamRef => arrayOfBounds(tpe.bounds)
case tpe: Type => preErase(tpe).arrayOf()
case tpe: Type => preErase(tpe, keepUnit = false).arrayOf()
case tpe: WildcardTypeArg => arrayOfBounds(tpe.bounds)
end arrayOf

Expand All @@ -74,40 +78,44 @@ private[tastyquery] object Erasure:
arrayOf(targ)
else ClassRef(cls)
case _ =>
preErase(tpe.translucentSuperType)
preErase(tpe.translucentSuperType, keepUnit)
case TypeRef.OfClass(cls) =>
ClassRef(cls)
if !keepUnit && cls == defn.UnitClass then ClassRef(defn.ErasedBoxedUnitClass)
else ClassRef(cls)
case tpe: TypeRef =>
tpe.optSymbol match
case Some(sym: TypeMemberSymbol) =>
sym.typeDef match
case TypeMemberDefinition.OpaqueTypeAlias(_, alias) =>
preErase(alias)
preErase(alias, keepUnit)
case _ =>
preErase(tpe.underlying)
preErase(tpe.underlying, keepUnit)
case _ =>
preErase(tpe.underlying)
preErase(tpe.underlying, keepUnit)
case tpe: SingletonType =>
preErase(tpe.underlying)
preErase(tpe.underlying, keepUnit)
case tpe: TypeParamRef =>
preErase(tpe.bounds.high)
preErase(tpe.bounds.high, keepUnit)
case tpe: MatchType =>
tpe.reduced match
case Some(reduced) => preErase(reduced)
case None => preErase(tpe.bound)
case Some(reduced) => preErase(reduced, keepUnit)
case None => preErase(tpe.bound, keepUnit)
case tpe: OrType =>
erasedLub(preErase(tpe.first), preErase(tpe.second))
erasedLub(preErase(tpe.first, keepUnit = false), preErase(tpe.second, keepUnit = false))
case tpe: AndType =>
summon[SourceLanguage] match
case SourceLanguage.Java => preErase(tpe.first)
case SourceLanguage.Scala2 => preErase(Scala2Erasure.eraseAndType(tpe))
case SourceLanguage.Scala3 => erasedGlb(preErase(tpe.first), preErase(tpe.second))
case SourceLanguage.Java =>
preErase(tpe.first, keepUnit = false)
case SourceLanguage.Scala2 =>
preErase(Scala2Erasure.eraseAndType(tpe), keepUnit = false)
case SourceLanguage.Scala3 =>
erasedGlb(preErase(tpe.first, keepUnit = false), preErase(tpe.second, keepUnit = false))
case tpe: AnnotatedType =>
preErase(tpe.typ)
preErase(tpe.typ, keepUnit)
case tpe: RefinedType =>
preErase(tpe.parent)
preErase(tpe.parent, keepUnit)
case tpe: RecType =>
preErase(tpe.parent)
preErase(tpe.parent, keepUnit)
case _: ByNameType =>
defn.Function0Class.erasure
case _: NothingType =>
Expand Down Expand Up @@ -168,14 +176,16 @@ private[tastyquery] object Erasure:
end erasedLub

private def erasedClassRefLub(cls1: ClassSymbol, cls2: ClassSymbol)(using Context): ClassSymbol =
if cls1 == defn.ErasedNothingClass then cls2
if cls1 == cls2 then cls1
else if cls1 == defn.ErasedNothingClass then cls2
else if cls2 == defn.ErasedNothingClass then cls1
else if cls1 == defn.NullClass then
if cls2.isSubClass(defn.ObjectClass) then cls2
else defn.AnyClass
else if cls2 == defn.NullClass then
if cls1.isSubClass(defn.ObjectClass) then cls1
else defn.AnyClass
else if cls1 == defn.ErasedBoxedUnitClass || cls2 == defn.ErasedBoxedUnitClass then defn.ObjectClass
else
/** takeWhile+1 */
def takeUpTo[T](l: List[T])(f: T => Boolean): List[T] =
Expand Down
4 changes: 4 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Names.scala
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ object Names {
val Tuple: TypeName = typeName("Tuple")
val NonEmptyTuple: TypeName = typeName("NonEmptyTuple")
val TupleCons: TypeName = typeName("*:")
val Enum: TypeName = typeName("Enum")

@deprecated("you probably meant the term name `nme.EmptyTuple` instead", since = "0.8.3")
val EmptyTuple: TypeName = typeName("EmptyTuple")
Expand All @@ -133,11 +134,14 @@ object Names {
val scala2PackageObjectClass: TypeName = termName("package").withObjectSuffix.toTypeName

private[tastyquery] val runtimeNothing: TypeName = typeName("Nothing$")
private[tastyquery] val runtimeBoxedUnit: TypeName = typeName("BoxedUnit")

private[tastyquery] val internalRepeatedAnnot: TypeName = typeName("Repeated")

private[tastyquery] val scala2LocalChild: TypeName = typeName("<local child>")
private[tastyquery] val scala2ByName: TypeName = typeName("<byname>")

private[tastyquery] val PredefModule: TypeName = moduleClassName("Predef")
}

/** Create a type name from a string */
Expand Down
5 changes: 3 additions & 2 deletions tasty-query/shared/src/main/scala/tastyquery/Signatures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ object Signatures:
case info: PolyType =>
rec(info.resultType, acc ::: ParamSig.TypeLen(info.paramTypeBounds.length) :: Nil)
case tpe: Type =>
val retType = optCtorReturn.map(_.localRef).getOrElse(tpe)
Signature(acc, ErasedTypeRef.erase(retType, language).toSigFullName)
val retType = optCtorReturn.map(_.appliedRefInsideThis).getOrElse(tpe)
val erasedRetType = ErasedTypeRef.erase(retType, language, keepUnit = true)
Signature(acc, erasedRetType.toSigFullName)
}

rec(info, Nil)
Expand Down
8 changes: 4 additions & 4 deletions tasty-query/shared/src/main/scala/tastyquery/Subtyping.scala
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,14 @@ private[tastyquery] object Subtyping:

private def level3(tp1: Type, tp2: Type)(using Context): Boolean = tp2 match
case TypeRef.OfClass(cls2) =>
if cls2.typeParams.isEmpty then
val tparams2 = cls2.typeParams
if tparams2.isEmpty then
if tp1.isLambdaSub then false // should be tp1.hasHigherKind, but the scalalib does not like that
else if cls2 == defn.AnyClass then true
else if cls2 == defn.SingletonClass && isSingleton(tp1) then true
else level3WithBaseType(tp1, tp2, cls2)
else
// TODO Try eta-expansion if tp1.isLambdaSub && !tp1.isAnyKind
level4(tp1, tp2)
else if tp1.isLambdaSub then isSubType(tp1, etaExpand(tp2, tparams2))
else level4(tp1, tp2)

case tp2: TypeRef =>
isSubType(tp1, tp2.bounds.low)
Expand Down
27 changes: 21 additions & 6 deletions tasty-query/shared/src/main/scala/tastyquery/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,8 @@ object Symbols {
final def isPublic: Boolean =
!flags.isAnyOf(Private | Protected | Local) && privateWithin.isEmpty

private[Symbols] final def isPrivateThis: Boolean =
flags.isAllOf(Private | Local)
private[Symbols] final def isPrivateParamAccessor: Boolean =
flags.isAllOf(Private | Local | ParamAccessor)

/** The declared visibility of this symbol. */
final def visibility: Visibility =
Expand Down Expand Up @@ -1156,9 +1156,9 @@ object Symbols {
if owner == defn.scalaPackage then
// The classes with special erasures that are loaded from Scala 2 pickles or .tasty files
name match
case tpnme.AnyVal | tpnme.TupleCons => defn.ObjectClass.erasure
case tpnme.Tuple | tpnme.NonEmptyTuple => defn.ProductClass.erasure
case _ => ErasedTypeRef.ClassRef(this)
case tpnme.AnyVal => defn.ObjectClass.erasure
case tpnme.Tuple | tpnme.NonEmptyTuple | tpnme.TupleCons => defn.ProductClass.erasure
case _ => ErasedTypeRef.ClassRef(this)
else ErasedTypeRef.ClassRef(this)
end computeErasure

Expand Down Expand Up @@ -1495,7 +1495,7 @@ object Symbols {
case _ => false

getDecl(name) match
case some @ Some(sym) if !sym.isPrivateThis || isOwnThis =>
case some @ Some(sym) if !sym.isPrivateParamAccessor || isOwnThis =>
some
case _ =>
if name == nme.Constructor then None
Expand Down Expand Up @@ -1628,6 +1628,21 @@ object Symbols {
case tpe =>
throw InvalidProgramStructureException(s"Unexpected type $tpe for $annot")
end extractSealedChildFromChildAnnot

private[tastyquery] def makePolyConstructorType(selfReferencingCtorType: TypeOrMethodic): TypeOrMethodic =
val typeParams = this.typeParams
if typeParams.isEmpty then selfReferencingCtorType
else
/* Make a PolyType with the same type parameters as the class, and
* substitute references to them of the form `C.this.T` by the
* corresponding `paramRefs` of the `PolyType`.
*/
PolyType(typeParams.map(_.name))(
pt =>
typeParams.map(p => Substituters.substLocalThisClassTypeParams(p.declaredBounds, typeParams, pt.paramRefs)),
pt => Substituters.substLocalThisClassTypeParams(selfReferencingCtorType, typeParams, pt.paramRefs)
)
end makePolyConstructorType
}

object ClassSymbol:
Expand Down
56 changes: 56 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,60 @@ private[tastyquery] object TypeOps:

loop(tp1.paramTypes, tp2.paramTypes)
end matchingMethodParams

// baseClasses

/** The set of classes inherited by this type, in linearization order. */
def baseClasses(tp: Type)(using Context): List[ClassSymbol] =
tp match
case TypeRef.OfClass(cls) =>
cls.linearization

case tp: TypeProxy =>
baseClasses(tp.superType)

case tp: AndType =>
// Base classes are the merge of the operand base classes.
val baseClasses1 = baseClasses(tp.first)
val baseClasses1Set = baseClasses1.toSet

def recur(baseClasses2: List[ClassSymbol]): List[ClassSymbol] = baseClasses2 match
case baseClass2 :: baseClasses2Rest =>
if baseClasses1Set.contains(baseClass2) then
// Don't add baseClass2 since it's already there; shortcut altogether if it is not a trait
if baseClass2.isTrait then recur(baseClasses2Rest)
else baseClasses1 // common class, therefore the rest is the same in both sequences
else
// Include baseClass2 and continue
baseClass2 :: recur(baseClasses2Rest)
case Nil =>
baseClasses1
end recur

recur(baseClasses(tp.second))

case tp: OrType =>
// Base classes are the intersection of the operand base classes.
// scala.Null is ignored on both sides
val baseClasses1 = baseClasses(tp.first).filter(_ != defn.NullClass)
val baseClasses1Set = baseClasses1.toSet

def recur(baseClasses2: List[ClassSymbol]): List[ClassSymbol] = baseClasses2 match
case baseClass2 :: baseClasses2Rest =>
if baseClasses1Set.contains(baseClass2) then
// Include baseClass2 which is common; shortcut altogether if it is not a trait
if baseClass2.isTrait then baseClass2 :: recur(baseClasses2Rest)
else baseClasses2 // common class, therefore the rest is the same in both sequences
else
// Exclude baseClass2 and continue
recur(baseClasses2Rest)
case Nil =>
Nil
end recur

recur(baseClasses(tp.second).filter(_ != defn.NullClass))

case _: AnyKindType | _: NothingType | _: TypeLambda | _: CustomTransientGroundType =>
Nil
end baseClasses
end TypeOps
33 changes: 32 additions & 1 deletion tasty-query/shared/src/main/scala/tastyquery/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ object Types {
def erase(tpe: Type, language: SourceLanguage)(using Context): ErasedTypeRef =
Erasure.erase(tpe, language)

def erase(tpe: Type, language: SourceLanguage, keepUnit: Boolean)(using Context): ErasedTypeRef =
Erasure.erase(tpe, language, keepUnit)

extension (typeRef: ArrayTypeRef)
def elemType: ErasedTypeRef =
if typeRef.dimensions == 1 then typeRef.base
Expand Down Expand Up @@ -2339,11 +2342,39 @@ object Types {
val myJoin = this.myJoin
if (myJoin != null) then myJoin
else
val computedJoin = defn.ObjectType // TypeOps.orDominator(this)
val computedJoin = computeJoin()
this.myJoin = computedJoin
computedJoin
}

private def computeJoin()(using Context): Type =
/** The minimal set of classes in `classes` which derive all other classes in `classes` */
def dominators(classes: List[ClassSymbol], acc: List[ClassSymbol]): List[ClassSymbol] = classes match
case cls :: rest =>
if acc.exists(_.isSubClass(cls)) then dominators(rest, acc)
else dominators(rest, cls :: acc)
case Nil =>
acc
end dominators

val prunedNulls = tryPruneNulls(this)

val commonBaseClasses = TypeOps.baseClasses(prunedNulls)
val doms = dominators(commonBaseClasses, Nil)
doms.flatMap(cls => prunedNulls.baseType(cls)).reduceLeft(AndType.make(_, _))
end computeJoin

private def tryPruneNulls(tp: Type)(using Context): Type = tp match
case tp: OrType =>
val first = tryPruneNulls(tp.first)
val second = tryPruneNulls(tp.second)
if first.isSubType(defn.NullType) && defn.NullType.isSubType(second) then second
else if second.isSubType(defn.NullType) && defn.NullType.isSubType(first) then first
else tp.derivedOrType(first, second)
case _ =>
tp
end tryPruneNulls

private[tastyquery] def resolveMember(name: Name, pre: Type)(using Context): ResolveMemberResult =
join.resolveMember(name, pre)

Expand Down
Loading

0 comments on commit 71b58e4

Please sign in to comment.