@@ -681,85 +681,133 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
681681 then
682682 report.error(StableIdentPattern (tree, pt), tree.srcPos)
683683
684- def typedSelect (tree0 : untpd.Select , pt : Type , qual : Tree )(using Context ): Tree =
684+ def typedSelectWithAdapt (tree0 : untpd.Select , pt : Type , qual : Tree )(using Context ): Tree =
685685 val selName = tree0.name
686686 val tree = cpy.Select (tree0)(qual, selName)
687687 val superAccess = qual.isInstanceOf [Super ]
688688 val rawType = selectionType(tree, qual)
689- val checkedType = accessibleType(rawType, superAccess)
690-
691- def finish (tree : untpd.Select , qual : Tree , checkedType : Type ): Tree =
692- val select = toNotNullTermRef(assignType(tree, checkedType), pt)
693- if selName.isTypeName then checkStable(qual.tpe, qual.srcPos, " type prefix" )
694- checkLegalValue(select, pt)
695- ConstFold (select)
696-
697- if checkedType.exists then
698- finish(tree, qual, checkedType)
699- else if selName == nme.apply && qual.tpe.widen.isInstanceOf [MethodType ] then
700- // Simplify `m.apply(...)` to `m(...)`
701- qual
702- else if couldInstantiateTypeVar(qual.tpe.widen) then
703- // there's a simply visible type variable in the result; try again with a more defined qualifier type
704- // There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
705- // but that is done only after we search for extension methods or conversions.
706- typedSelect(tree, pt, qual)
707- else if qual.tpe.isSmallGenericTuple then
708- val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil )
709- typedSelect(tree, pt, qual.cast(defn.tupleType(elems)))
710- else
711- val tree1 = tryExtensionOrConversion(
712- tree, pt, IgnoredProto (pt), qual, ctx.typerState.ownedVars, this , inSelect = true )
713- .orElse {
714- if ctx.gadt.isNarrowing then
715- // try GADT approximation if we're trying to select a member
716- // Member lookup cannot take GADTs into account b/c of cache, so we
717- // approximate types based on GADT constraints instead. For an example,
718- // see MemberHealing in gadt-approximation-interaction.scala.
719- val wtp = qual.tpe.widen
720- gadts.println(i " Trying to heal member selection by GADT-approximating $wtp" )
721- val gadtApprox = Inferencing .approximateGADT(wtp)
722- gadts.println(i " GADT-approximated $wtp ~~ $gadtApprox" )
723- val qual1 = qual.cast(gadtApprox)
724- val tree1 = cpy.Select (tree0)(qual1, selName)
725- val checkedType1 = accessibleType(selectionType(tree1, qual1), superAccess = false )
726- if checkedType1.exists then
727- gadts.println(i " Member selection healed by GADT approximation " )
728- finish(tree1, qual1, checkedType1)
729- else if qual1.tpe.isSmallGenericTuple then
730- gadts.println(i " Tuple member selection healed by GADT approximation " )
731- typedSelect(tree, pt, qual1)
732- else
733- tryExtensionOrConversion(tree1, pt, IgnoredProto (pt), qual1, ctx.typerState.ownedVars, this , inSelect = true )
734- else EmptyTree
735- }
736- if ! tree1.isEmpty then
737- tree1
738- else if canDefineFurther(qual.tpe.widen) then
739- typedSelect(tree, pt, qual)
740- else if qual.tpe.derivesFrom(defn.DynamicClass )
741- && selName.isTermName && ! isDynamicExpansion(tree)
742- then
689+
690+ def tryType (tree : untpd.Select , qual : Tree , rawType : Type ) =
691+ val checkedType = accessibleType(rawType, superAccess)
692+ // If regular selection is typeable, we are done
693+ if checkedType.exists then
694+ val select = toNotNullTermRef(assignType(tree, checkedType), pt)
695+ if selName.isTypeName then checkStable(qual.tpe, qual.srcPos, " type prefix" )
696+ checkLegalValue(select, pt)
697+ ConstFold (select)
698+ else EmptyTree
699+
700+ // Otherwise, simplify `m.apply(...)` to `m(...)`
701+ def trySimplifyApply () =
702+ if selName == nme.apply && qual.tpe.widen.isInstanceOf [MethodType ] then
703+ qual
704+ else EmptyTree
705+
706+ // Otherwise, if there's a simply visible type variable in the result, try again
707+ // with a more defined qualifier type. There's a second trial where we try to instantiate
708+ // all type variables in `qual.tpe.widen`, but that is done only after we search for
709+ // extension methods or conversions.
710+ def tryInstantiateTypeVar () =
711+ if couldInstantiateTypeVar(qual.tpe.widen) then
712+ // there's a simply visible type variable in the result; try again with a more defined qualifier type
713+ // There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
714+ // but that is done only after we search for extension methods or conversions.
715+ typedSelectWithAdapt(tree, pt, qual)
716+ else EmptyTree
717+
718+ // Otherwise, heal member selection on an opaque reference,
719+ // reusing the logic in TypeComparer.
720+ def tryLiftToThis () =
721+ val wtp = qual.tpe.widen
722+ val liftedTp = comparing(_.liftToThis(wtp))
723+ if liftedTp ne wtp then
724+ val qual1 = qual.cast(liftedTp)
725+ val tree1 = cpy.Select (tree0)(qual1, selName)
726+ val rawType1 = selectionType(tree1, qual1)
727+ tryType(tree1, qual1, rawType1)
728+ else EmptyTree
729+
730+ // Otherwise, map combinations of A *: B *: .... EmptyTuple with nesting levels <= 22
731+ // to the Tuple class of the right arity and select from that one
732+ def trySmallGenericTuple (qual : Tree , withCast : Boolean ) =
733+ if qual.tpe.isSmallGenericTuple then
734+ if withCast then
735+ val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil )
736+ typedSelectWithAdapt(tree, pt, qual.cast(defn.tupleType(elems)))
737+ else
738+ typedSelectWithAdapt(tree, pt, qual)
739+ else EmptyTree
740+
741+ // Otherwise try an extension or conversion
742+ def tryExt (tree : untpd.Select , qual : Tree ) =
743+ tryExtensionOrConversion(
744+ tree, pt, IgnoredProto (pt), qual, ctx.typerState.ownedVars, this , inSelect = true
745+ )
746+
747+ // Otherwise, try a GADT approximation if we're trying to select a member
748+ def tryGadt () =
749+ if ctx.gadt.isNarrowing then
750+ // Member lookup cannot take GADTs into account b/c of cache, so we
751+ // approximate types based on GADT constraints instead. For an example,
752+ // see MemberHealing in gadt-approximation-interaction.scala.
753+ val wtp = qual.tpe.widen
754+ gadts.println(i " Trying to heal member selection by GADT-approximating $wtp" )
755+ val gadtApprox = Inferencing .approximateGADT(wtp)
756+ gadts.println(i " GADT-approximated $wtp ~~ $gadtApprox" )
757+ val qual1 = qual.cast(gadtApprox)
758+ val tree1 = cpy.Select (tree0)(qual1, selName)
759+ tryType(tree1, qual1, selectionType(tree1, qual1))
760+ .orElse(trySmallGenericTuple(qual1, withCast = false ))
761+ .orElse(tryExt(tree1, qual1))
762+ else EmptyTree
763+
764+ // Otherwise, if there are uninstantiated type variables in the qualifier type,
765+ // instantiate them and try again
766+ def tryDefineFurther () =
767+ if canDefineFurther(qual.tpe.widen) then
768+ typedSelectWithAdapt(tree, pt, qual)
769+ else EmptyTree
770+
771+ def dynamicSelect (pt : Type ) =
743772 val tree2 = cpy.Select (tree0)(untpd.TypedSplice (qual), selName)
744773 if pt.isInstanceOf [FunOrPolyProto ] || pt == AssignProto then
745774 assignType(tree2, TryDynamicCallType )
746775 else
747776 typedDynamicSelect(tree2, Nil , pt)
748- else
749- assignType(tree,
750- rawType match
751- case rawType : NamedType =>
752- inaccessibleErrorType(rawType, superAccess, tree.srcPos)
753- case _ =>
754- notAMemberErrorType(tree, qual, pt))
755- end typedSelect
777+
778+ // Otherwise, if the qualifier derives from class Dynamic, expand to a
779+ // dynamic dispatch using selectDynamic or applyDynamic
780+ def tryDynamic () =
781+ if qual.tpe.derivesFrom(defn.DynamicClass ) && selName.isTermName && ! isDynamicExpansion(tree) then
782+ dynamicSelect(pt)
783+ else EmptyTree
784+
785+ def reportAnError () =
786+ assignType(tree,
787+ rawType match
788+ case rawType : NamedType =>
789+ inaccessibleErrorType(rawType, superAccess, tree.srcPos)
790+ case _ =>
791+ notAMemberErrorType(tree, qual, pt))
792+
793+ tryType(tree, qual, rawType)
794+ .orElse(trySimplifyApply())
795+ .orElse(tryInstantiateTypeVar())
796+ .orElse(tryLiftToThis())
797+ .orElse(trySmallGenericTuple(qual, withCast = true ))
798+ .orElse(tryExt(tree, qual))
799+ .orElse(tryGadt())
800+ .orElse(tryDefineFurther())
801+ .orElse(tryDynamic())
802+ .orElse(reportAnError())
803+ end typedSelectWithAdapt
756804
757805 def typedSelect (tree : untpd.Select , pt : Type )(using Context ): Tree = {
758806 record(" typedSelect" )
759807
760808 def typeSelectOnTerm (using Context ): Tree =
761809 val qual = typedExpr(tree.qualifier, shallowSelectionProto(tree.name, pt, this , tree.nameSpan))
762- typedSelect (tree, pt, qual).withSpan(tree.span).computeNullable()
810+ typedSelectWithAdapt (tree, pt, qual).withSpan(tree.span).computeNullable()
763811
764812 def javaSelectOnType (qual : Tree )(using Context ) =
765813 // semantic name conversion for `O$` in java code
@@ -3563,7 +3611,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
35633611 if isExtension then return found
35643612 else
35653613 checkImplicitConversionUseOK(found, selProto)
3566- return withoutMode(Mode .ImplicitsEnabled )(typedSelect (tree, pt, found))
3614+ return withoutMode(Mode .ImplicitsEnabled )(typedSelectWithAdapt (tree, pt, found))
35673615 case failure : SearchFailure =>
35683616 if failure.isAmbiguous then
35693617 return
0 commit comments