@@ -724,137 +724,158 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
724
724
then
725
725
report.error(StableIdentPattern (tree, pt), tree.srcPos)
726
726
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 =
728
728
val selName = tree0.name
729
729
val tree = cpy.Select (tree0)(qual, selName)
730
730
val superAccess = qual.isInstanceOf [Super ]
731
731
val rawType = selectionType(tree, qual)
732
- val checkedType = accessibleType(rawType, superAccess)
733
732
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
743
742
744
743
// 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
747
748
748
749
// Otherwise, if there's a simply visible type variable in the result, try again
749
750
// with a more defined qualifier type. There's a second trial where we try to instantiate
750
751
// all type variables in `qual.tpe.widen`, but that is done only after we search for
751
752
// 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
757
760
758
761
// 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
767
772
768
773
// Otherwise, map combinations of A *: B *: .... EmptyTuple with nesting levels <= 22
769
774
// 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
773
783
774
784
// 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(
777
787
tree, pt, IgnoredProto (pt), qual, ctx.typerState.ownedVars, this , inSelect = true )
778
- if ! tree1.isEmpty then
779
- return tree1
780
788
781
789
// 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
804
805
805
806
// Otherwise, if there are uninstantiated type variables in the qualifier type,
806
807
// 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
809
812
810
813
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)
816
819
817
820
// Otherwise, if the qualifier derives from class Dynamic, expand to a
818
821
// 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
821
826
822
827
// Otherwise, if the qualifier derives from class Selectable,
823
828
// and the selector name matches one of the element of the `Fields` type member,
824
829
// and the selector is not assigned to,
825
830
// 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
843
850
844
851
// Otherwise, if the qualifier is a context bound companion, handle
845
852
// 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
849
857
850
858
// 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
858
879
859
880
/** Expand a selection A.m on a context bound companion A with type
860
881
* `<context-bound-companion>[ref_1 | ... | ref_N]` as described by
@@ -938,7 +959,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
938
959
if ctx.isJava then
939
960
javaSelection(qual)
940
961
else
941
- typedSelect (tree, pt, qual).withSpan(tree.span).computeNullable()
962
+ typedSelectWithAdapt (tree, pt, qual).withSpan(tree.span).computeNullable()
942
963
943
964
def javaSelection (qual : Tree )(using Context ) =
944
965
val tree1 = assignType(cpy.Select (tree)(qual, tree.name), qual)
@@ -3879,7 +3900,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
3879
3900
if isExtension then return found
3880
3901
else
3881
3902
checkImplicitConversionUseOK(found, selProto)
3882
- return withoutMode(Mode .ImplicitsEnabled )(typedSelect (tree, pt, found))
3903
+ return withoutMode(Mode .ImplicitsEnabled )(typedSelectWithAdapt (tree, pt, found))
3883
3904
case failure : SearchFailure =>
3884
3905
if failure.isAmbiguous then
3885
3906
return
0 commit comments