Skip to content

Commit c477cea

Browse files
committed
Reorganise typedSelectWithAdapt
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.
1 parent 6230405 commit c477cea

File tree

1 file changed

+114
-90
lines changed

1 file changed

+114
-90
lines changed

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 114 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -729,131 +729,155 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
729729
val tree = cpy.Select(tree0)(qual, selName)
730730
val superAccess = qual.isInstanceOf[Super]
731731
val rawType = selectionType(tree, qual)
732-
val checkedType = accessibleType(rawType, superAccess)
733732

734-
def finish(tree: untpd.Select, qual: Tree, checkedType: Type): Tree =
735-
val select = toNotNullTermRef(assignType(tree, checkedType), pt)
736-
if selName.isTypeName then checkStable(qual.tpe, qual.srcPos, "type prefix")
737-
checkLegalValue(select, pt)
738-
ConstFold(select)
739-
740-
// If regular selection is typeable, we are done
741-
if checkedType.exists then
742-
return finish(tree, qual, checkedType)
733+
def tryType(tree: untpd.Select, qual: Tree, rawType: Type) =
734+
val checkedType = accessibleType(rawType, superAccess)
735+
// If regular selection is typeable, we are done
736+
if checkedType.exists then
737+
val select = toNotNullTermRef(assignType(tree, checkedType), pt)
738+
if selName.isTypeName then checkStable(qual.tpe, qual.srcPos, "type prefix")
739+
checkLegalValue(select, pt)
740+
ConstFold(select)
741+
else EmptyTree
743742

744743
// Otherwise, simplify `m.apply(...)` to `m(...)`
745-
if selName == nme.apply && qual.tpe.widen.isInstanceOf[MethodType] then
746-
return qual
744+
def trySimplifyApply() =
745+
if selName == nme.apply && qual.tpe.widen.isInstanceOf[MethodType] then
746+
qual
747+
else EmptyTree
747748

748749
// Otherwise, if there's a simply visible type variable in the result, try again
749750
// with a more defined qualifier type. There's a second trial where we try to instantiate
750751
// all type variables in `qual.tpe.widen`, but that is done only after we search for
751752
// extension methods or conversions.
752-
if couldInstantiateTypeVar(qual.tpe.widen) then
753-
// there's a simply visible type variable in the result; try again with a more defined qualifier type
754-
// There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
755-
// but that is done only after we search for extension methods or conversions.
756-
return typedSelectWithAdapt(tree, pt, qual)
753+
def tryInstantiateTypeVar() =
754+
if couldInstantiateTypeVar(qual.tpe.widen) then
755+
// there's a simply visible type variable in the result; try again with a more defined qualifier type
756+
// There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
757+
// but that is done only after we search for extension methods or conversions.
758+
typedSelectWithAdapt(tree, pt, qual)
759+
else EmptyTree
757760

758761
// Otherwise, try to expand a named tuple selection
759-
val namedTupleElems = qual.tpe.widenDealias.namedTupleElementTypes
760-
val nameIdx = namedTupleElems.indexWhere(_._1 == selName)
761-
if nameIdx >= 0 && Feature.enabled(Feature.namedTuples) then
762-
return typed(
763-
untpd.Apply(
764-
untpd.Select(untpd.TypedSplice(qual), nme.apply),
765-
untpd.Literal(Constant(nameIdx))),
766-
pt)
762+
def tryNamedTupleSelection() =
763+
val namedTupleElems = qual.tpe.widenDealias.namedTupleElementTypes
764+
val nameIdx = namedTupleElems.indexWhere(_._1 == selName)
765+
if nameIdx >= 0 && Feature.enabled(Feature.namedTuples) then
766+
typed(
767+
untpd.Apply(
768+
untpd.Select(untpd.TypedSplice(qual), nme.apply),
769+
untpd.Literal(Constant(nameIdx))),
770+
pt)
771+
else EmptyTree
767772

768773
// Otherwise, map combinations of A *: B *: .... EmptyTuple with nesting levels <= 22
769774
// to the Tuple class of the right arity and select from that one
770-
if qual.tpe.isSmallGenericTuple then
771-
val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil)
772-
return typedSelectWithAdapt(tree, pt, qual.cast(defn.tupleType(elems)))
775+
def trySmallGenericTuple(qual: Tree, withCast: Boolean) =
776+
if qual.tpe.isSmallGenericTuple then
777+
if withCast then
778+
val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil)
779+
typedSelectWithAdapt(tree, pt, qual.cast(defn.tupleType(elems)))
780+
else
781+
typedSelectWithAdapt(tree, pt, qual)
782+
else EmptyTree
773783

774784
// Otherwise try an extension or conversion
775-
if selName.isTermName then
776-
val tree1 = tryExtensionOrConversion(
777-
tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true)
778-
if !tree1.isEmpty then
779-
return tree1
785+
def tryExt(tree: untpd.Select, qual: Tree) =
786+
if selName.isTermName then
787+
tryExtensionOrConversion(
788+
tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true)
789+
else EmptyTree
780790

781791
// Otherwise, try a GADT approximation if we're trying to select a member
782-
// Member lookup cannot take GADTs into account b/c of cache, so we
783-
// approximate types based on GADT constraints instead. For an example,
784-
// see MemberHealing in gadt-approximation-interaction.scala.
785-
if ctx.gadt.isNarrowing then
786-
val wtp = qual.tpe.widen
787-
gadts.println(i"Trying to heal member selection by GADT-approximating $wtp")
788-
val gadtApprox = Inferencing.approximateGADT(wtp)
789-
gadts.println(i"GADT-approximated $wtp ~~ $gadtApprox")
790-
val qual1 = qual.cast(gadtApprox)
791-
val tree1 = cpy.Select(tree0)(qual1, selName)
792-
val checkedType1 = accessibleType(selectionType(tree1, qual1), superAccess = false)
793-
if checkedType1.exists then
794-
gadts.println(i"Member selection healed by GADT approximation")
795-
return finish(tree1, qual1, checkedType1)
796-
797-
if qual1.tpe.isSmallGenericTuple then
798-
gadts.println(i"Tuple member selection healed by GADT approximation")
799-
return typedSelectWithAdapt(tree, pt, qual1)
800-
801-
val tree2 = tryExtensionOrConversion(tree1, pt, IgnoredProto(pt), qual1, ctx.typerState.ownedVars, this, inSelect = true)
802-
if !tree2.isEmpty then
803-
return tree2
792+
def tryGadt() =
793+
if ctx.gadt.isNarrowing then
794+
// Member lookup cannot take GADTs into account b/c of cache, so we
795+
// approximate types based on GADT constraints instead. For an example,
796+
// see MemberHealing in gadt-approximation-interaction.scala.
797+
val wtp = qual.tpe.widen
798+
gadts.println(i"Trying to heal member selection by GADT-approximating $wtp")
799+
val gadtApprox = Inferencing.approximateGADT(wtp)
800+
gadts.println(i"GADT-approximated $wtp ~~ $gadtApprox")
801+
val qual1 = qual.cast(gadtApprox)
802+
val tree1 = cpy.Select(tree0)(qual1, selName)
803+
tryType(tree1, qual1, selectionType(tree1, qual1))
804+
.orElse(trySmallGenericTuple(qual1, withCast = false))
805+
.orElse(tryExt(tree1, qual1))
806+
else EmptyTree
804807

805808
// Otherwise, if there are uninstantiated type variables in the qualifier type,
806809
// instantiate them and try again
807-
if canDefineFurther(qual.tpe.widen) then
808-
return typedSelectWithAdapt(tree, pt, qual)
810+
def tryDefineFurther() =
811+
if canDefineFurther(qual.tpe.widen) then
812+
typedSelectWithAdapt(tree, pt, qual)
813+
else EmptyTree
809814

810815
def dynamicSelect(pt: Type) =
811-
val tree2 = cpy.Select(tree0)(untpd.TypedSplice(qual), selName)
812-
if pt.isInstanceOf[FunOrPolyProto] || pt == LhsProto then
813-
assignType(tree2, TryDynamicCallType)
814-
else
815-
typedDynamicSelect(tree2, Nil, pt)
816+
val tree2 = cpy.Select(tree0)(untpd.TypedSplice(qual), selName)
817+
if pt.isInstanceOf[FunOrPolyProto] || pt == LhsProto then
818+
assignType(tree2, TryDynamicCallType)
819+
else
820+
typedDynamicSelect(tree2, Nil, pt)
816821

817822
// Otherwise, if the qualifier derives from class Dynamic, expand to a
818823
// dynamic dispatch using selectDynamic or applyDynamic
819-
if qual.tpe.derivesFrom(defn.DynamicClass) && selName.isTermName && !isDynamicExpansion(tree) then
820-
return dynamicSelect(pt)
824+
def tryDynamic() =
825+
if qual.tpe.derivesFrom(defn.DynamicClass) && selName.isTermName && !isDynamicExpansion(tree) then
826+
dynamicSelect(pt)
827+
else EmptyTree
821828

822829
// Otherwise, if the qualifier derives from class Selectable,
823830
// and the selector name matches one of the element of the `Fields` type member,
824831
// and the selector is not assigned to,
825832
// expand to a typed dynamic dispatch using selectDynamic wrapped in a cast
826-
if qual.tpe.derivesFrom(defn.SelectableClass) && !isDynamicExpansion(tree)
827-
&& pt != LhsProto
828-
then
829-
val pre = if !TypeOps.isLegalPrefix(qual.tpe) then SkolemType(qual.tpe) else qual.tpe
830-
val fieldsType = pre.select(tpnme.Fields).dealias.simplified
831-
val fields = fieldsType.namedTupleElementTypes
832-
typr.println(i"try dyn select $qual, $selName, $fields")
833-
fields.find(_._1 == selName) match
834-
case Some((_, fieldType)) =>
835-
val dynSelected = dynamicSelect(fieldType)
836-
dynSelected match
837-
case Apply(sel: Select, _) if !sel.denot.symbol.exists =>
838-
// Reject corner case where selectDynamic needs annother selectDynamic to be called. E.g. as in neg/unselectable-fields.scala.
839-
report.error(i"Cannot use selectDynamic here since it needs another selectDynamic to be invoked", tree.srcPos)
840-
case _ =>
841-
return dynSelected.ensureConforms(fieldType)
842-
case _ =>
833+
def trySelectable() =
834+
if qual.tpe.derivesFrom(defn.SelectableClass) && !isDynamicExpansion(tree)
835+
&& pt != LhsProto
836+
then
837+
val pre = if !TypeOps.isLegalPrefix(qual.tpe) then SkolemType(qual.tpe) else qual.tpe
838+
val fieldsType = pre.select(tpnme.Fields).dealias.simplified
839+
val fields = fieldsType.namedTupleElementTypes
840+
typr.println(i"try dyn select $qual, $selName, $fields")
841+
fields.find(_._1 == selName) match
842+
case Some((_, fieldType)) =>
843+
val dynSelected = dynamicSelect(fieldType)
844+
dynSelected match
845+
case Apply(sel: Select, _) if !sel.denot.symbol.exists =>
846+
// Reject corner case where selectDynamic needs annother selectDynamic to be called. E.g. as in neg/unselectable-fields.scala.
847+
report.error(i"Cannot use selectDynamic here since it needs another selectDynamic to be invoked", tree.srcPos)
848+
case _ =>
849+
dynSelected.ensureConforms(fieldType)
850+
case _ => EmptyTree
851+
else EmptyTree
843852

844853
// Otherwise, if the qualifier is a context bound companion, handle
845854
// by selecting a witness in typedCBSelect
846-
if qual.tpe.typeSymbol == defn.CBCompanion then
847-
val witnessSelection = typedCBSelect(tree0, pt, qual)
848-
if !witnessSelection.isEmpty then return witnessSelection
855+
def tryCBCompanion() =
856+
if qual.tpe.typeSymbol == defn.CBCompanion then
857+
typedCBSelect(tree0, pt, qual)
858+
else EmptyTree
849859

850860
// Otherwise, report an error
851-
assignType(tree,
852-
rawType match
853-
case rawType: NamedType =>
854-
inaccessibleErrorType(rawType, superAccess, tree.srcPos)
855-
case _ =>
856-
notAMemberErrorType(tree, qual, pt))
861+
def reportAnError() =
862+
assignType(tree,
863+
rawType match
864+
case rawType: NamedType =>
865+
inaccessibleErrorType(rawType, superAccess, tree.srcPos)
866+
case _ =>
867+
notAMemberErrorType(tree, qual, pt))
868+
869+
tryType(tree, qual, rawType)
870+
.orElse(trySimplifyApply())
871+
.orElse(tryInstantiateTypeVar())
872+
.orElse(tryNamedTupleSelection())
873+
.orElse(trySmallGenericTuple(qual, withCast = true))
874+
.orElse(tryExt(tree, qual))
875+
.orElse(tryGadt())
876+
.orElse(tryDefineFurther())
877+
.orElse(tryDynamic())
878+
.orElse(trySelectable())
879+
.orElse(tryCBCompanion())
880+
.orElse(reportAnError())
857881
end typedSelectWithAdapt
858882

859883
/** Expand a selection A.m on a context bound companion A with type

0 commit comments

Comments
 (0)