Skip to content

Commit

Permalink
Reorganise typedSelectWithAdapt
Browse files Browse the repository at this point in the history
Prior to the next commit, I broke up the logic into internal methods, so
some can be reused, consuming then in a big Tree#orElse chain.  I also
took the opportunity to rename the method, to more easily distinguish it
from the other typedSelect.

[Cherry-picked c477cea][modified]
  • Loading branch information
WojciechMazur committed Jul 8, 2024
1 parent e2b18ff commit 3dc65c6
Showing 1 changed file with 84 additions and 60 deletions.
144 changes: 84 additions & 60 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -686,72 +686,96 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
val tree = cpy.Select(tree0)(qual, selName)
val superAccess = qual.isInstanceOf[Super]
val rawType = selectionType(tree, qual)
val checkedType = accessibleType(rawType, superAccess)

def finish(tree: untpd.Select, qual: Tree, checkedType: Type): Tree =
val select = toNotNullTermRef(assignType(tree, checkedType), pt)
if selName.isTypeName then checkStable(qual.tpe, qual.srcPos, "type prefix")
checkLegalValue(select, pt)
ConstFold(select)

if checkedType.exists then
finish(tree, qual, checkedType)
else if selName == nme.apply && qual.tpe.widen.isInstanceOf[MethodType] then
// Simplify `m.apply(...)` to `m(...)`
qual
else if couldInstantiateTypeVar(qual.tpe.widen) then
// there's a simply visible type variable in the result; try again with a more defined qualifier type
// There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
// but that is done only after we search for extension methods or conversions.
typedSelectWithAdapt(tree, pt, qual)
else if qual.tpe.isSmallGenericTuple then
val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil)
typedSelectWithAdapt(tree, pt, qual.cast(defn.tupleType(elems)))
else
val tree1 = tryExtensionOrConversion(
tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true)
.orElse {
if ctx.gadt.isNarrowing then
// try GADT approximation if we're trying to select a member
// Member lookup cannot take GADTs into account b/c of cache, so we
// approximate types based on GADT constraints instead. For an example,
// see MemberHealing in gadt-approximation-interaction.scala.
val wtp = qual.tpe.widen
gadts.println(i"Trying to heal member selection by GADT-approximating $wtp")
val gadtApprox = Inferencing.approximateGADT(wtp)
gadts.println(i"GADT-approximated $wtp ~~ $gadtApprox")
val qual1 = qual.cast(gadtApprox)
val tree1 = cpy.Select(tree0)(qual1, selName)
val checkedType1 = accessibleType(selectionType(tree1, qual1), superAccess = false)
if checkedType1.exists then
gadts.println(i"Member selection healed by GADT approximation")
finish(tree1, qual1, checkedType1)
else if qual1.tpe.isSmallGenericTuple then
gadts.println(i"Tuple member selection healed by GADT approximation")
typedSelectWithAdapt(tree, pt, qual1)
else
tryExtensionOrConversion(tree1, pt, IgnoredProto(pt), qual1, ctx.typerState.ownedVars, this, inSelect = true)
else EmptyTree
}
if !tree1.isEmpty then
tree1
else if canDefineFurther(qual.tpe.widen) then

def tryType(tree: untpd.Select, qual: Tree, rawType: Type) =
val checkedType = accessibleType(rawType, superAccess)
// If regular selection is typeable, we are done
if checkedType.exists then
val select = toNotNullTermRef(assignType(tree, checkedType), pt)
if selName.isTypeName then checkStable(qual.tpe, qual.srcPos, "type prefix")
checkLegalValue(select, pt)
ConstFold(select)
else EmptyTree

def trySimplifyApply() =
if selName == nme.apply && qual.tpe.widen.isInstanceOf[MethodType] then
// Simplify `m.apply(...)` to `m(...)`
qual
else EmptyTree

def tryInstantiateTypeVar() =
if couldInstantiateTypeVar(qual.tpe.widen) then
// there's a simply visible type variable in the result; try again with a more defined qualifier type
// There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
// but that is done only after we search for extension methods or conversions.
typedSelectWithAdapt(tree, pt, qual)
else if qual.tpe.derivesFrom(defn.DynamicClass)
&& selName.isTermName && !isDynamicExpansion(tree)
then
else EmptyTree

def trySmallGenericTuple(qual: Tree, withCast: Boolean) =
if qual.tpe.isSmallGenericTuple then
if withCast then
val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil)
typedSelectWithAdapt(tree, pt, qual.cast(defn.tupleType(elems)))
else
typedSelectWithAdapt(tree, pt, qual)
else EmptyTree

def tryExt(tree: untpd.Select, qual: Tree) =
tryExtensionOrConversion(
tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true
)

def tryGadt() =
if ctx.gadt.isNarrowing then
// try GADT approximation if we're trying to select a member
// Member lookup cannot take GADTs into account b/c of cache, so we
// approximate types based on GADT constraints instead. For an example,
// see MemberHealing in gadt-approximation-interaction.scala.
val wtp = qual.tpe.widen
gadts.println(i"Trying to heal member selection by GADT-approximating $wtp")
val gadtApprox = Inferencing.approximateGADT(wtp)
gadts.println(i"GADT-approximated $wtp ~~ $gadtApprox")
val qual1 = qual.cast(gadtApprox)
val tree1 = cpy.Select(tree0)(qual1, selName)
tryType(tree1, qual1, selectionType(tree1, qual1))
.orElse(trySmallGenericTuple(qual1, withCast = false))
.orElse(tryExt(tree1, qual1))
else EmptyTree

def tryDefineFurther() =
if canDefineFurther(qual.tpe.widen) then
typedSelectWithAdapt(tree, pt, qual)
else EmptyTree

def dynamicSelect(pt: Type) =
val tree2 = cpy.Select(tree0)(untpd.TypedSplice(qual), selName)
if pt.isInstanceOf[FunOrPolyProto] || pt == AssignProto then
assignType(tree2, TryDynamicCallType)
else
typedDynamicSelect(tree2, Nil, pt)
else
assignType(tree,
rawType match
case rawType: NamedType =>
inaccessibleErrorType(rawType, superAccess, tree.srcPos)
case _ =>
notAMemberErrorType(tree, qual, pt))

def tryDynamic() =
if qual.tpe.derivesFrom(defn.DynamicClass) && selName.isTermName && !isDynamicExpansion(tree) then
dynamicSelect(pt)
else EmptyTree

def reportAnError() =
assignType(tree,
rawType match
case rawType: NamedType =>
inaccessibleErrorType(rawType, superAccess, tree.srcPos)
case _ =>
notAMemberErrorType(tree, qual, pt))

tryType(tree, qual, rawType)
.orElse(trySimplifyApply())
.orElse(tryInstantiateTypeVar())
.orElse(trySmallGenericTuple(qual, withCast = true))
.orElse(tryExt(tree, qual))
.orElse(tryGadt())
.orElse(tryDefineFurther())
.orElse(tryDynamic())
.orElse(reportAnError())
end typedSelectWithAdapt

def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = {
Expand Down

0 comments on commit 3dc65c6

Please sign in to comment.