Skip to content

Commit 3f7a3fa

Browse files
committed
Reorganise (& rename) 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 f7ab683 commit 3f7a3fa

File tree

1 file changed

+114
-93
lines changed

1 file changed

+114
-93
lines changed

Diff for: compiler/src/dotty/tools/dotc/typer/Typer.scala

+114-93
Original file line numberDiff line numberDiff line change
@@ -724,137 +724,158 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
724724
then
725725
report.error(StableIdentPattern(tree, pt), tree.srcPos)
726726

727-
def typedSelect(tree0: untpd.Select, pt: Type, qual: Tree)(using Context): Tree =
727+
def typedSelectWithAdapt(tree0: untpd.Select, pt: Type, qual: Tree)(using Context): Tree =
728728
val selName = tree0.name
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 typedSelect(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 typedSelect(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(
785+
def tryExt(tree: untpd.Select, qual: Tree) =
786+
tryExtensionOrConversion(
777787
tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true)
778-
if !tree1.isEmpty then
779-
return tree1
780788

781789
// 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 typedSelect(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
790+
def tryGadt(tree: untpd.Select) =
791+
if ctx.gadt.isNarrowing then
792+
// Member lookup cannot take GADTs into account b/c of cache, so we
793+
// approximate types based on GADT constraints instead. For an example,
794+
// see MemberHealing in gadt-approximation-interaction.scala.
795+
val wtp = qual.tpe.widen
796+
gadts.println(i"Trying to heal member selection by GADT-approximating $wtp")
797+
val gadtApprox = Inferencing.approximateGADT(wtp)
798+
gadts.println(i"GADT-approximated $wtp ~~ $gadtApprox")
799+
val qual1 = qual.cast(gadtApprox)
800+
val tree1 = cpy.Select(tree0)(qual1, selName)
801+
tryType(tree1, qual1, selectionType(tree1, qual1))
802+
.orElse(trySmallGenericTuple(qual1, withCast = false))
803+
.orElse(tryExt(tree1, qual1))
804+
else EmptyTree
804805

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

810813
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)
814+
val tree2 = cpy.Select(tree0)(untpd.TypedSplice(qual), selName)
815+
if pt.isInstanceOf[FunOrPolyProto] || pt == LhsProto then
816+
assignType(tree2, TryDynamicCallType)
817+
else
818+
typedDynamicSelect(tree2, Nil, pt)
816819

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

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

844851
// Otherwise, if the qualifier is a context bound companion, handle
845852
// 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
853+
def tryCBCompanion() =
854+
if qual.tpe.typeSymbol == defn.CBCompanion then
855+
val witnessSelection = typedCBSelect(tree0, pt, qual)
856+
if !witnessSelection.isEmpty then return witnessSelection
849857

850858
// 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))
857-
end typedSelect
859+
def reportAnError() =
860+
assignType(tree,
861+
rawType match
862+
case rawType: NamedType =>
863+
inaccessibleErrorType(rawType, superAccess, tree.srcPos)
864+
case _ =>
865+
notAMemberErrorType(tree, qual, pt))
866+
867+
tryType(tree, qual, rawType)
868+
.orElse(trySimplifyApply())
869+
.orElse(tryInstantiateTypeVar())
870+
.orElse(tryNamedTupleSelection())
871+
.orElse(trySmallGenericTuple(qual, withCast = true))
872+
.orElse(tryExt(tree, qual))
873+
.orElse(tryGadt(tree))
874+
.orElse(tryDefineFurther())
875+
.orElse(tryDynamic())
876+
.orElse(tryCBCompanion())
877+
.orElse(reportAnError())
878+
end typedSelectWithAdapt
858879

859880
/** Expand a selection A.m on a context bound companion A with type
860881
* `<context-bound-companion>[ref_1 | ... | ref_N]` as described by
@@ -938,7 +959,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
938959
if ctx.isJava then
939960
javaSelection(qual)
940961
else
941-
typedSelect(tree, pt, qual).withSpan(tree.span).computeNullable()
962+
typedSelectWithAdapt(tree, pt, qual).withSpan(tree.span).computeNullable()
942963

943964
def javaSelection(qual: Tree)(using Context) =
944965
val tree1 = assignType(cpy.Select(tree)(qual, tree.name), qual)
@@ -3879,7 +3900,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38793900
if isExtension then return found
38803901
else
38813902
checkImplicitConversionUseOK(found, selProto)
3882-
return withoutMode(Mode.ImplicitsEnabled)(typedSelect(tree, pt, found))
3903+
return withoutMode(Mode.ImplicitsEnabled)(typedSelectWithAdapt(tree, pt, found))
38833904
case failure: SearchFailure =>
38843905
if failure.isAmbiguous then
38853906
return

0 commit comments

Comments
 (0)