@@ -724,137 +724,174 @@ 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
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
757
772
758
773
// 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
767
784
768
785
// Otherwise, map combinations of A *: B *: .... EmptyTuple with nesting levels <= 22
769
786
// 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
773
795
774
796
// 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
780
802
781
803
// 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
804
819
805
820
// Otherwise, if there are uninstantiated type variables in the qualifier type,
806
821
// 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
809
826
810
827
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)
816
833
817
834
// Otherwise, if the qualifier derives from class Dynamic, expand to a
818
835
// 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
821
840
822
841
// Otherwise, if the qualifier derives from class Selectable,
823
842
// and the selector name matches one of the element of the `Fields` type member,
824
843
// and the selector is not assigned to,
825
844
// 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
843
864
844
865
// Otherwise, if the qualifier is a context bound companion, handle
845
866
// 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
849
871
850
872
// 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
858
895
859
896
/** Expand a selection A.m on a context bound companion A with type
860
897
* `<context-bound-companion>[ref_1 | ... | ref_N]` as described by
@@ -906,7 +943,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
906
943
case witness : TermRef =>
907
944
val altQual = tpd.ref(witness).withSpan(qual.span)
908
945
val altCtx = ctx.fresh.setNewTyperState()
909
- val alt = typedSelect (tree, pt, altQual)(using altCtx)
946
+ val alt = typedSelectWithAdapt (tree, pt, altQual)(using altCtx)
910
947
def current = (alt, altCtx.typerState, witness)
911
948
if altCtx.reporter.hasErrors then prevs
912
949
else
@@ -938,7 +975,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
938
975
if ctx.isJava then
939
976
javaSelection(qual)
940
977
else
941
- typedSelect (tree, pt, qual).withSpan(tree.span).computeNullable()
978
+ typedSelectWithAdapt (tree, pt, qual).withSpan(tree.span).computeNullable()
942
979
943
980
def javaSelection (qual : Tree )(using Context ) =
944
981
val tree1 = assignType(cpy.Select (tree)(qual, tree.name), qual)
@@ -3879,7 +3916,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
3879
3916
if isExtension then return found
3880
3917
else
3881
3918
checkImplicitConversionUseOK(found, selProto)
3882
- return withoutMode(Mode .ImplicitsEnabled )(typedSelect (tree, pt, found))
3919
+ return withoutMode(Mode .ImplicitsEnabled )(typedSelectWithAdapt (tree, pt, found))
3883
3920
case failure : SearchFailure =>
3884
3921
if failure.isAmbiguous then
3885
3922
return
0 commit comments