@@ -724,137 +724,174 @@ 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
760+
761+ // Otherwise, heal member selection on an opaque reference,
762+ // reusing the logic in TypeComparer.
763+ def tryLiftToThis () =
764+ val wtp = qual.tpe.widen
765+ val liftedTp = comparing(_.liftToThis(wtp))
766+ if liftedTp ne wtp then
767+ val qual1 = qual.cast(liftedTp)
768+ val tree1 = cpy.Select (tree0)(qual1, selName)
769+ val rawType1 = selectionType(tree1, qual1)
770+ tryType(tree1, qual1, rawType1)
771+ else EmptyTree
757772
758773 // 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)
774+ def tryNamedTupleSelection () =
775+ val namedTupleElems = qual.tpe.widenDealias.namedTupleElementTypes
776+ val nameIdx = namedTupleElems.indexWhere(_._1 == selName)
777+ if nameIdx >= 0 && Feature .enabled(Feature .namedTuples) then
778+ typed(
779+ untpd.Apply (
780+ untpd.Select (untpd.TypedSplice (qual), nme.apply),
781+ untpd.Literal (Constant (nameIdx))),
782+ pt)
783+ else EmptyTree
767784
768785 // Otherwise, map combinations of A *: B *: .... EmptyTuple with nesting levels <= 22
769786 // 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)))
787+ def trySmallGenericTuple (qual : Tree , withCast : Boolean ) =
788+ if qual.tpe.isSmallGenericTuple then
789+ if withCast then
790+ val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil )
791+ typedSelectWithAdapt(tree, pt, qual.cast(defn.tupleType(elems)))
792+ else
793+ typedSelectWithAdapt(tree, pt, qual)
794+ else EmptyTree
773795
774796 // 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
797+ def tryExt ( tree : untpd. Select , qual : Tree ) =
798+ if selName.isTermName then
799+ tryExtensionOrConversion(
800+ tree, pt, IgnoredProto (pt), qual, ctx.typerState.ownedVars, this , inSelect = true )
801+ else EmptyTree
780802
781803 // 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
804+ def tryGadt () =
805+ if ctx.gadt.isNarrowing then
806+ // Member lookup cannot take GADTs into account b/c of cache, so we
807+ // approximate types based on GADT constraints instead. For an example,
808+ // see MemberHealing in gadt-approximation-interaction.scala.
809+ val wtp = qual.tpe.widen
810+ gadts.println(i " Trying to heal member selection by GADT-approximating $wtp" )
811+ val gadtApprox = Inferencing .approximateGADT(wtp)
812+ gadts.println(i " GADT-approximated $wtp ~~ $gadtApprox" )
813+ val qual1 = qual.cast(gadtApprox)
814+ val tree1 = cpy.Select (tree0)(qual1, selName)
815+ tryType(tree1, qual1, selectionType(tree1, qual1))
816+ .orElse(trySmallGenericTuple(qual1, withCast = false ))
817+ .orElse(tryExt(tree1, qual1))
818+ else EmptyTree
804819
805820 // Otherwise, if there are uninstantiated type variables in the qualifier type,
806821 // instantiate them and try again
807- if canDefineFurther(qual.tpe.widen) then
808- return typedSelect(tree, pt, qual)
822+ def tryDefineFurther () =
823+ if canDefineFurther(qual.tpe.widen) then
824+ typedSelectWithAdapt(tree, pt, qual)
825+ else EmptyTree
809826
810827 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)
828+ val tree2 = cpy.Select (tree0)(untpd.TypedSplice (qual), selName)
829+ if pt.isInstanceOf [FunOrPolyProto ] || pt == LhsProto then
830+ assignType(tree2, TryDynamicCallType )
831+ else
832+ typedDynamicSelect(tree2, Nil , pt)
816833
817834 // Otherwise, if the qualifier derives from class Dynamic, expand to a
818835 // dynamic dispatch using selectDynamic or applyDynamic
819- if qual.tpe.derivesFrom(defn.DynamicClass ) && selName.isTermName && ! isDynamicExpansion(tree) then
820- return dynamicSelect(pt)
836+ def tryDynamic () =
837+ if qual.tpe.derivesFrom(defn.DynamicClass ) && selName.isTermName && ! isDynamicExpansion(tree) then
838+ dynamicSelect(pt)
839+ else EmptyTree
821840
822841 // Otherwise, if the qualifier derives from class Selectable,
823842 // and the selector name matches one of the element of the `Fields` type member,
824843 // and the selector is not assigned to,
825844 // 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 _ =>
845+ def trySelectable () =
846+ if qual.tpe.derivesFrom(defn.SelectableClass ) && ! isDynamicExpansion(tree)
847+ && pt != LhsProto
848+ then
849+ val pre = if ! TypeOps .isLegalPrefix(qual.tpe) then SkolemType (qual.tpe) else qual.tpe
850+ val fieldsType = pre.select(tpnme.Fields ).dealias.simplified
851+ val fields = fieldsType.namedTupleElementTypes
852+ typr.println(i " try dyn select $qual, $selName, $fields" )
853+ fields.find(_._1 == selName) match
854+ case Some ((_, fieldType)) =>
855+ val dynSelected = dynamicSelect(fieldType)
856+ dynSelected match
857+ case Apply (sel : Select , _) if ! sel.denot.symbol.exists =>
858+ // Reject corner case where selectDynamic needs annother selectDynamic to be called. E.g. as in neg/unselectable-fields.scala.
859+ report.error(i " Cannot use selectDynamic here since it needs another selectDynamic to be invoked " , tree.srcPos)
860+ case _ =>
861+ dynSelected.ensureConforms(fieldType)
862+ case _ => EmptyTree
863+ else EmptyTree
843864
844865 // Otherwise, if the qualifier is a context bound companion, handle
845866 // 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
867+ def tryCBCompanion () =
868+ if qual.tpe.typeSymbol == defn.CBCompanion then
869+ typedCBSelect(tree0, pt, qual)
870+ else EmptyTree
849871
850872 // 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
873+ def reportAnError () =
874+ assignType(tree,
875+ rawType match
876+ case rawType : NamedType =>
877+ inaccessibleErrorType(rawType, superAccess, tree.srcPos)
878+ case _ =>
879+ notAMemberErrorType(tree, qual, pt))
880+
881+ tryType(tree, qual, rawType)
882+ .orElse(trySimplifyApply())
883+ .orElse(tryInstantiateTypeVar())
884+ .orElse(tryLiftToThis())
885+ .orElse(tryNamedTupleSelection())
886+ .orElse(trySmallGenericTuple(qual, withCast = true ))
887+ .orElse(tryExt(tree, qual))
888+ .orElse(tryGadt())
889+ .orElse(tryDefineFurther())
890+ .orElse(tryDynamic())
891+ .orElse(trySelectable())
892+ .orElse(tryCBCompanion())
893+ .orElse(reportAnError())
894+ end typedSelectWithAdapt
858895
859896 /** Expand a selection A.m on a context bound companion A with type
860897 * `<context-bound-companion>[ref_1 | ... | ref_N]` as described by
@@ -906,7 +943,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
906943 case witness : TermRef =>
907944 val altQual = tpd.ref(witness).withSpan(qual.span)
908945 val altCtx = ctx.fresh.setNewTyperState()
909- val alt = typedSelect (tree, pt, altQual)(using altCtx)
946+ val alt = typedSelectWithAdapt (tree, pt, altQual)(using altCtx)
910947 def current = (alt, altCtx.typerState, witness)
911948 if altCtx.reporter.hasErrors then prevs
912949 else
@@ -938,7 +975,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
938975 if ctx.isJava then
939976 javaSelection(qual)
940977 else
941- typedSelect (tree, pt, qual).withSpan(tree.span).computeNullable()
978+ typedSelectWithAdapt (tree, pt, qual).withSpan(tree.span).computeNullable()
942979
943980 def javaSelection (qual : Tree )(using Context ) =
944981 val tree1 = assignType(cpy.Select (tree)(qual, tree.name), qual)
@@ -3879,7 +3916,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38793916 if isExtension then return found
38803917 else
38813918 checkImplicitConversionUseOK(found, selProto)
3882- return withoutMode(Mode .ImplicitsEnabled )(typedSelect (tree, pt, found))
3919+ return withoutMode(Mode .ImplicitsEnabled )(typedSelectWithAdapt (tree, pt, found))
38833920 case failure : SearchFailure =>
38843921 if failure.isAmbiguous then
38853922 return
0 commit comments