Skip to content

Commit

Permalink
Merge pull request #282 from sjrd/fix-issues-from-checker-3
Browse files Browse the repository at this point in the history
Fix a few more issues discovered by the TASTy Checker
  • Loading branch information
bishabosha authored Apr 4, 2023
2 parents 1086459 + 363a883 commit 6d0923a
Show file tree
Hide file tree
Showing 9 changed files with 424 additions and 41 deletions.
5 changes: 5 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ lazy val tastyQuery =
ProblemFilters.exclude[DirectMissingMethodProblem]("tastyquery.Symbols#ClassSymbol.createRefinedClassSymbol"),
ProblemFilters.exclude[FinalClassProblem]("tastyquery.TypeOps$AsSeenFromMap"),
ProblemFilters.exclude[IncompatibleMethTypeProblem]("tastyquery.TypeOps#AsSeenFromMap.this"),
ProblemFilters.exclude[DirectMissingMethodProblem]("tastyquery.*.makeResolveMemberResult"),

// private[tastyquery] and implemented in the entire "open boundary" of Type, so this is fine
ProblemFilters.exclude[DirectMissingMethodProblem]("tastyquery.Types#Type.resolveMatchingMember"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("tastyquery.Types#Type.resolveMatchingMember"),
)
},
)
Expand Down
3 changes: 3 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Signatures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ object Signatures:
case ParamSig.Term(typ) => typ.toString
case ParamSig.TypeLen(len) => len.toString
}.mkString("(", ",", ")") + ":" + resSig.toString

private[tastyquery] def paramsCorrespond(that: Signature): Boolean =
this.paramsSig == that.paramsSig
end Signature

object Signature {
Expand Down
63 changes: 45 additions & 18 deletions tasty-query/shared/src/main/scala/tastyquery/Subtyping.scala
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,22 @@ private[tastyquery] object Subtyping:
val etaExpandSuccess = tparams1.nonEmpty && isSubtype(etaExpand(tp1, tparams1), tp2)
etaExpandSuccess || level4(tp1, tp2)

case tp2: MethodType =>
tp1 match
case tp1: MethodType =>
TypeOps.matchingMethodParams(tp1, tp2)
&& isSubtype(tp1.resultType, Substituters.substBinders(tp2.resultType, tp2, tp1))
case _ =>
false

case tp2: PolyType =>
tp1 match
case tp1: PolyType =>
TypeOps.matchingPolyParams(tp1, tp2)
&& isSubtype(tp1.resultType, Substituters.substBinders(tp2.resultType, tp2, tp1))
case _ =>
false

case tp2: RefinedType =>
(isSubtype(tp1, tp2.parent) && hasMatchingRefinedMember(tp1, tp2))
|| level4(tp1, tp2)
Expand Down Expand Up @@ -297,24 +313,35 @@ private[tastyquery] object Subtyping:
TypeLambda.fromParamInfos(tparams)(tl => tp.appliedTo(tl.paramRefs))

private def hasMatchingRefinedMember(tp1: Type, tp2: RefinedType)(using Context): Boolean =
tp1.resolveMember(tp2.refinedName, tp1) match
case ResolveMemberResult.NotFound =>
false

case ResolveMemberResult.TermMember(symbols, tpe) =>
tp2 match
case tp2: TermRefinement => isSubtype(tpe, tp2.refinedType)
case _: TypeRefinement => throw AssertionError(s"found term member for $tp2 in $tp1")

case ResolveMemberResult.ClassMember(cls) =>
tp2 match
case tp2: TypeRefinement => tp2.refinedBounds.contains(tp1.select(cls))
case _: TermRefinement => throw AssertionError(s"found type member for $tp2 in $tp1")

case ResolveMemberResult.TypeMember(symbols, bounds) =>
tp2 match
case tp2: TypeRefinement => tp2.refinedBounds.contains(bounds)
case _: TermRefinement => throw AssertionError(s"found type member for $tp2 in $tp1")
tp2 match
case tp2: TypeRefinement =>
tp1.resolveMember(tp2.refinedName, tp1) match
case ResolveMemberResult.NotFound =>
false
case ResolveMemberResult.ClassMember(cls) =>
tp2.refinedBounds.contains(tp1.select(cls))
case ResolveMemberResult.TypeMember(symbols, bounds) =>
tp2.refinedBounds.contains(bounds)
case ResolveMemberResult.TermMember(symbols, tpe) =>
throw AssertionError(s"found term member for $tp2 in $tp1")

case tp2: TermRefinement =>
if !tp2.isMethodic then
tp1.resolveMember(tp2.refinedName, tp1) match
case ResolveMemberResult.NotFound =>
false
case ResolveMemberResult.TermMember(_, tpe) =>
tpe.isSubtype(tp2.refinedType)
case _: ResolveMemberResult.ClassMember | _: ResolveMemberResult.TypeMember =>
throw AssertionError(s"found type member for $tp2 in $tp1")
else
tp1.resolveMatchingMember(tp2.signedName, tp1, _.isSubtype(tp2.refinedType)) match
case ResolveMemberResult.NotFound =>
false
case _: ResolveMemberResult.TermMember =>
true
case _: ResolveMemberResult.ClassMember | _: ResolveMemberResult.TypeMember =>
throw AssertionError(s"found type member for $tp2 in $tp1")
end hasMatchingRefinedMember

private def level4(tp1: Type, tp2: Type)(using Context): Boolean = tp1 match
Expand Down
53 changes: 50 additions & 3 deletions tasty-query/shared/src/main/scala/tastyquery/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,25 @@ object Symbols {
idx += 1
}
None

case Some(base: AndType) =>
(argForParam(base.first), argForParam(base.second)) match
case (None, tp2) =>
tp2
case (tp1, None) =>
tp1
case (Some(tp1), Some(tp2)) =>
val variance = this.paramVariance.sign
val result: Type =
if tp1.isInstanceOf[WildcardTypeBounds] || tp2.isInstanceOf[WildcardTypeBounds] || variance == 0 then
// TODO? Compute based on bounds, instead of returning the original reference
TypeRef(pre, this)
else if variance > 0 then tp1 & tp2
else tp1 | tp2
end result
Some(result)
end match

/*case base: AndOrType =>
var tp1 = argForParam(base.tp1)
var tp2 = argForParam(base.tp2)
Expand All @@ -573,6 +592,7 @@ object Symbols {
tp2 = tp2.bounds
}
if (base.isAnd == variance >= 0) tp1 & tp2 else tp1 | tp2*/

case _ =>
/*if (pre.termSymbol.isPackage) argForParam(pre.select(nme.PACKAGE))
else*/
Expand Down Expand Up @@ -729,16 +749,16 @@ object Symbols {
* > Drops package objects. Represents each term in the owner chain by a simple `_$`.
*
* The code actually represents each *non-class* in the owner chain by a simple `_$`.
* Moreover, there does not seem to be any code that actually drops package objects,
* and evidence suggests that it does not.
*/
private[tastyquery] final def signatureName: FullyQualifiedName =
def computeErasedName(owner: Symbol, name: TypeName): FullyQualifiedName = owner match
case owner: PackageSymbol =>
owner.fullName.select(name)

case owner: ClassSymbol =>
// Drop package objects
if owner.name.isPackageObjectClassName && owner.owner.isPackage then owner.owner.fullName.select(name)
else owner.signatureName.mapLast(_.toTermName).select(name)
owner.signatureName.mapLast(_.toTermName).select(name)

case owner: TermOrTypeSymbol =>
// Replace non-class non-package owners by simple `_$`
Expand Down Expand Up @@ -1130,6 +1150,33 @@ object Symbols {
ResolveMemberResult.NotFound
end resolveMember

private[tastyquery] def resolveMatchingMember(name: SignedName, pre: Type, typePredicate: Type => Boolean)(
using Context
): ResolveMemberResult =
def lookup(lin: List[ClassSymbol]): ResolveMemberResult = lin match
case parentCls :: linRest =>
var overloadsRest = parentCls.getAllOverloadedDecls(name.underlying)
while overloadsRest.nonEmpty do
val decl = overloadsRest.head
val matches =
!decl.isAnyOf(Private | Protected | Local)
&& decl.needsSignature
&& name.sig.paramsCorrespond(decl.signature)
if matches then
val tpe = decl.declaredTypeAsSeenFrom(pre)
if typePredicate(tpe) then return ResolveMemberResult.TermMember(decl :: Nil, tpe)
end if
overloadsRest = overloadsRest.tail
end while
lookup(linRest)

case Nil =>
ResolveMemberResult.NotFound
end lookup

lookup(linearization)
end resolveMatchingMember

private var myTypeRef: TypeRef | Null = null

private[tastyquery] final def typeRef(using Context): TypeRef =
Expand Down
13 changes: 11 additions & 2 deletions tasty-query/shared/src/main/scala/tastyquery/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private[tastyquery] object TypeOps:
case tp1: PolyType =>
tp2.widen match
case tp2: PolyType =>
tp1.paramNames.lengthCompare(tp2.paramNames) == 0
matchingPolyParams(tp1, tp2)
&& matchesType(tp1.resultType, Substituters.substBinders(tp2.resultType, tp2, tp1))
case _ =>
false
Expand All @@ -115,11 +115,20 @@ private[tastyquery] object TypeOps:
true
end matchesType

/** Do the parameter types of `tp1` and `tp2` match in a way that allows `tp1` to override `tp2`?
*
* This is the case if they're pairwise `>:>`.
*/
def matchingPolyParams(tp1: PolyType, tp2: PolyType)(using Context): Boolean =
// TODO Actually test `>:>`.
tp1.paramNames.lengthCompare(tp2.paramNames) == 0
end matchingPolyParams

/** Do the parameter types of `tp1` and `tp2` match in a way that allows `tp1` to override `tp2`?
*
* This is the case if they're pairwise `=:=`.
*/
private def matchingMethodParams(tp1: MethodType, tp2: MethodType)(using Context): Boolean =
def matchingMethodParams(tp1: MethodType, tp2: MethodType)(using Context): Boolean =
def loop(formals1: List[Type], formals2: List[Type]): Boolean = formals1 match
case formal1 :: rest1 =>
formals2 match
Expand Down
Loading

0 comments on commit 6d0923a

Please sign in to comment.