Skip to content

Commit 5a31ba1

Browse files
committed
Refactor typedSelect
I usually try to avoid explicit returns, but here they do make the code easier to read.
1 parent cb9aa46 commit 5a31ba1

File tree

1 file changed

+98
-75
lines changed

1 file changed

+98
-75
lines changed

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

Lines changed: 98 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -721,86 +721,109 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
721721
checkLegalValue(select, pt)
722722
ConstFold(select)
723723

724+
// If regular selection is typeable, we are done
724725
if checkedType.exists then
725-
finish(tree, qual, checkedType)
726-
else if selName == nme.apply && qual.tpe.widen.isInstanceOf[MethodType] then
727-
// Simplify `m.apply(...)` to `m(...)`
728-
qual
729-
else if couldInstantiateTypeVar(qual.tpe.widen) then
726+
return finish(tree, qual, checkedType)
727+
728+
// Otherwise, simplify `m.apply(...)` to `m(...)`
729+
if selName == nme.apply && qual.tpe.widen.isInstanceOf[MethodType] then
730+
return qual
731+
732+
// Otherwise, if there's a simply visible type variable in the result, try again
733+
// with a more defined qualifier type. There's a second trial where we try to instantiate
734+
// all type variables in `qual.tpe.widen`, but that is done only after we search for
735+
// extension methods or conversions.
736+
if couldInstantiateTypeVar(qual.tpe.widen) then
730737
// there's a simply visible type variable in the result; try again with a more defined qualifier type
731738
// There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
732739
// but that is done only after we search for extension methods or conversions.
733-
typedSelect(tree, pt, qual)
734-
else
735-
val namedTupleElems = qual.tpe.widen.namedTupleElementTypes
736-
val nameIdx = namedTupleElems.indexWhere(_._1 == selName)
737-
if nameIdx >= 0 && Feature.enabled(Feature.namedTuples) then
738-
typed(
739-
untpd.Apply(
740-
untpd.Select(untpd.TypedSplice(qual), nme.apply),
741-
untpd.Literal(Constant(nameIdx))),
742-
pt)
743-
else if qual.tpe.isSmallGenericTuple then
744-
val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil)
745-
typedSelect(tree, pt, qual.cast(defn.tupleType(elems)))
740+
return typedSelect(tree, pt, qual)
741+
742+
// Otherwise, try to expand a named tuple selection
743+
val namedTupleElems = qual.tpe.widen.namedTupleElementTypes
744+
val nameIdx = namedTupleElems.indexWhere(_._1 == selName)
745+
if nameIdx >= 0 && Feature.enabled(Feature.namedTuples) then
746+
return typed(
747+
untpd.Apply(
748+
untpd.Select(untpd.TypedSplice(qual), nme.apply),
749+
untpd.Literal(Constant(nameIdx))),
750+
pt)
751+
752+
// Otherwise, map combinations of A *: B *: .... EmptyTuple with nesting levels <= 22
753+
// to the Tuple class of the right arity and select from that one
754+
if qual.tpe.isSmallGenericTuple then
755+
val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil)
756+
return typedSelect(tree, pt, qual.cast(defn.tupleType(elems)))
757+
758+
// Otherwise try an extension or conversion
759+
val tree1 = tryExtensionOrConversion(
760+
tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true)
761+
if !tree1.isEmpty then
762+
return tree1
763+
764+
// Otherwise, try a GADT approximation if we're trying to select a member
765+
// Member lookup cannot take GADTs into account b/c of cache, so we
766+
// approximate types based on GADT constraints instead. For an example,
767+
// see MemberHealing in gadt-approximation-interaction.scala.
768+
if ctx.gadt.isNarrowing then
769+
val wtp = qual.tpe.widen
770+
gadts.println(i"Trying to heal member selection by GADT-approximating $wtp")
771+
val gadtApprox = Inferencing.approximateGADT(wtp)
772+
gadts.println(i"GADT-approximated $wtp ~~ $gadtApprox")
773+
val qual1 = qual.cast(gadtApprox)
774+
val tree1 = cpy.Select(tree0)(qual1, selName)
775+
val checkedType1 = accessibleType(selectionType(tree1, qual1), superAccess = false)
776+
if checkedType1.exists then
777+
gadts.println(i"Member selection healed by GADT approximation")
778+
return finish(tree1, qual1, checkedType1)
779+
780+
if qual1.tpe.isSmallGenericTuple then
781+
gadts.println(i"Tuple member selection healed by GADT approximation")
782+
return typedSelect(tree, pt, qual1)
783+
784+
val tree2 = tryExtensionOrConversion(tree1, pt, IgnoredProto(pt), qual1, ctx.typerState.ownedVars, this, inSelect = true)
785+
if !tree2.isEmpty then
786+
return tree2
787+
788+
// Otherwise, if there are uninstantiated type variables in the qualifier type,
789+
// instantiate them and try again
790+
if canDefineFurther(qual.tpe.widen) then
791+
return typedSelect(tree, pt, qual)
792+
793+
def dynamicSelect =
794+
val tree2 = cpy.Select(tree0)(untpd.TypedSplice(qual), selName)
795+
if pt.isInstanceOf[FunOrPolyProto] || pt == LhsProto then
796+
assignType(tree2, TryDynamicCallType)
746797
else
747-
val tree1 = tryExtensionOrConversion(
748-
tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true)
749-
.orElse {
750-
if ctx.gadt.isNarrowing then
751-
// try GADT approximation if we're trying to select a member
752-
// Member lookup cannot take GADTs into account b/c of cache, so we
753-
// approximate types based on GADT constraints instead. For an example,
754-
// see MemberHealing in gadt-approximation-interaction.scala.
755-
val wtp = qual.tpe.widen
756-
gadts.println(i"Trying to heal member selection by GADT-approximating $wtp")
757-
val gadtApprox = Inferencing.approximateGADT(wtp)
758-
gadts.println(i"GADT-approximated $wtp ~~ $gadtApprox")
759-
val qual1 = qual.cast(gadtApprox)
760-
val tree1 = cpy.Select(tree0)(qual1, selName)
761-
val checkedType1 = accessibleType(selectionType(tree1, qual1), superAccess = false)
762-
if checkedType1.exists then
763-
gadts.println(i"Member selection healed by GADT approximation")
764-
finish(tree1, qual1, checkedType1)
765-
else if qual1.tpe.isSmallGenericTuple then
766-
gadts.println(i"Tuple member selection healed by GADT approximation")
767-
typedSelect(tree, pt, qual1)
768-
else
769-
tryExtensionOrConversion(tree1, pt, IgnoredProto(pt), qual1, ctx.typerState.ownedVars, this, inSelect = true)
770-
else EmptyTree
771-
}
772-
if !tree1.isEmpty then
773-
tree1
774-
else if canDefineFurther(qual.tpe.widen) then
775-
typedSelect(tree, pt, qual)
776-
else if qual.tpe.derivesFrom(defn.DynamicClass)
777-
&& selName.isTermName && !isDynamicExpansion(tree)
778-
then
779-
val tree2 = cpy.Select(tree0)(untpd.TypedSplice(qual), selName)
780-
if pt.isInstanceOf[FunOrPolyProto] || pt == LhsProto then
781-
assignType(tree2, TryDynamicCallType)
782-
else
783-
typedDynamicSelect(tree2, Nil, pt)
784-
else
785-
if qual.tpe.derivesFrom(defn.SelectableClass)
786-
&& selName.isTermName && !isDynamicExpansion(tree)
787-
&& !pt.isInstanceOf[FunOrPolyProto] && pt != LhsProto
788-
then
789-
val fieldsType = qual.tpe.select(tpnme.Fields).dealias.simplified
790-
val fields = fieldsType.namedTupleElementTypes
791-
typr.println(i"try dyn select $qual, $selName, $fields")
792-
fields.find(_._1 == selName) match
793-
case Some((fieldName, fieldType)) =>
794-
val tree2 = cpy.Select(tree0)(untpd.TypedSplice(qual), selName)
795-
val sel = typedDynamicSelect(tree2, Nil, pt)
796-
return sel.cast(fieldType)
797-
case _ =>
798-
assignType(tree,
799-
rawType match
800-
case rawType: NamedType =>
801-
inaccessibleErrorType(rawType, superAccess, tree.srcPos)
802-
case _ =>
803-
notAMemberErrorType(tree, qual, pt))
798+
typedDynamicSelect(tree2, Nil, pt)
799+
800+
// Otherwise, if the qualifier derives from class Dynamic, expand to a
801+
// dynamic dispatch using selectDynamic or applyDynamic
802+
if qual.tpe.derivesFrom(defn.DynamicClass) && selName.isTermName && !isDynamicExpansion(tree) then
803+
return dynamicSelect
804+
805+
// Otherwise, if the qualifier derives from class Selectable,
806+
// and the selector name matches one of the element of the `Fields` type member,
807+
// and the selector is neither applied nor assigned to,
808+
// expand to a typed dynamic dispatch using selectDynamic wrapped in a cast
809+
if qual.tpe.derivesFrom(defn.SelectableClass) && !isDynamicExpansion(tree)
810+
&& !pt.isInstanceOf[FunOrPolyProto] && pt != LhsProto
811+
then
812+
val fieldsType = qual.tpe.select(tpnme.Fields).dealias.simplified
813+
val fields = fieldsType.namedTupleElementTypes
814+
typr.println(i"try dyn select $qual, $selName, $fields")
815+
fields.find(_._1 == selName) match
816+
case Some((_, fieldType)) =>
817+
return dynamicSelect.cast(fieldType)
818+
case _ =>
819+
820+
// Otherwise, report an error
821+
assignType(tree,
822+
rawType match
823+
case rawType: NamedType =>
824+
inaccessibleErrorType(rawType, superAccess, tree.srcPos)
825+
case _ =>
826+
notAMemberErrorType(tree, qual, pt))
804827
end typedSelect
805828

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

0 commit comments

Comments
 (0)