@@ -30,7 +30,7 @@ import reporting.diagnostic.{Message, MessageContainer}
30
30
import Inferencing .fullyDefinedType
31
31
import Trees ._
32
32
import Hashable ._
33
- import util .Property
33
+ import util .{ Property , PriorityGraph }
34
34
import config .Config
35
35
import config .Printers .{implicits , implicitsDetailed , typr }
36
36
import collection .mutable
@@ -257,6 +257,10 @@ object Implicits {
257
257
sealed abstract class SearchResult extends Showable {
258
258
def tree : tpd.Tree
259
259
def toText (printer : Printer ): Text = printer.toText(this )
260
+ def orElse (other : => SearchResult ) = this match {
261
+ case _ : SearchSuccess => this
262
+ case _ => other
263
+ }
260
264
}
261
265
262
266
/** A successful search
@@ -316,9 +320,9 @@ object Implicits {
316
320
SearchFailure (NoMatchingImplicits )
317
321
318
322
/** An ambiguous implicits failure */
319
- class AmbiguousImplicits (val alt1 : TermRef , val alt2 : TermRef , val expectedType : Type , val argument : tpd.Tree ) extends SearchFailureType {
323
+ class AmbiguousImplicits (val alt1 : SearchSuccess , val alt2 : SearchSuccess , val expectedType : Type , val argument : tpd.Tree ) extends SearchFailureType {
320
324
def explanation (implicit ctx : Context ): String =
321
- em " both ${err.refStr(alt1)} and ${err.refStr(alt2)} $qualify"
325
+ em " both ${err.refStr(alt1.ref )} and ${err.refStr(alt2.ref )} $qualify"
322
326
override def whyNoConversion (implicit ctx : Context ) =
323
327
" \n Note that implicit conversions cannot be applied because they are ambiguous;" +
324
328
" \n " + explanation
@@ -750,17 +754,19 @@ trait Implicits { self: Typer =>
750
754
751
755
/** An implicit search; parameters as in `inferImplicit` */
752
756
class ImplicitSearch (protected val pt : Type , protected val argument : Tree , pos : Position )(implicit ctx : Context ) {
757
+ assert(argument.isEmpty || argument.tpe.isValueType || argument.tpe.isInstanceOf [ExprType ],
758
+ em " found: $argument: ${argument.tpe}, expected: $pt" )
753
759
754
- private def nestedContext = ctx.fresh.setMode(ctx.mode &~ Mode .ImplicitsEnabled )
760
+ private def nestedContext () = ctx.fresh.setMode(ctx.mode &~ Mode .ImplicitsEnabled )
755
761
756
762
private def implicitProto (resultType : Type , f : Type => Type ) =
757
763
if (argument.isEmpty) f(resultType) else ViewProto (f(argument.tpe.widen), f(resultType))
758
764
// Not clear whether we need to drop the `.widen` here. All tests pass with it in place, though.
759
765
760
766
private def isCoherent = pt.isRef(defn.EqClass )
761
767
762
- assert(argument.isEmpty || argument.tpe.isValueType || argument.tpe. isInstanceOf [ ExprType ],
763
- em " found: $argument : ${argument.tpe} , expected: $pt " )
768
+ private val cmpContext = nestedContext()
769
+ private val cmpCandidates = ( c1 : Candidate , c2 : Candidate ) => compare(c1.ref, c2.ref, c1.level, c2.level)(cmpContext )
764
770
765
771
/** The expected type for the searched implicit */
766
772
lazy val fullProto = implicitProto(pt, identity)
@@ -778,6 +784,8 @@ trait Implicits { self: Typer =>
778
784
def searchImplicits (eligible : List [Candidate ], contextual : Boolean ): SearchResult = {
779
785
val constr = ctx.typerState.constraint
780
786
787
+ // println(i"search implicits $pt / ${eligible.map(_.ref)}")
788
+
781
789
/** Try to typecheck an implicit reference */
782
790
def typedImplicit (cand : Candidate )(implicit ctx : Context ): SearchResult = track(" typedImplicit" ) { trace(i " typed implicit ${cand.ref}, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled }" , implicits, show = true ) {
783
791
assert(constr eq ctx.typerState.constraint)
@@ -790,7 +798,7 @@ trait Implicits { self: Typer =>
790
798
val generated1 = adapt(generated, pt)
791
799
lazy val shadowing =
792
800
typed(untpd.Ident (ref.name) withPos pos.toSynthetic, funProto)(
793
- nestedContext.addMode(Mode .ImplicitShadowing ).setExploreTyperState())
801
+ nestedContext() .addMode(Mode .ImplicitShadowing ).setExploreTyperState())
794
802
def refSameAs (shadowing : Tree ): Boolean =
795
803
ref.symbol == closureBody(shadowing).symbol || {
796
804
shadowing match {
@@ -820,6 +828,126 @@ trait Implicits { self: Typer =>
820
828
SearchSuccess (generated1, ref, cand.level)(ctx.typerState)
821
829
}}
822
830
831
+ def tryImplicit (cand : Candidate ): SearchResult = {
832
+ val history = ctx.searchHistory nest wildProto
833
+ if (history eq ctx.searchHistory)
834
+ SearchFailure (new DivergingImplicit (cand.ref, pt, argument))
835
+ else
836
+ typedImplicit(cand)(nestedContext().setNewTyperState().setSearchHistory(history))
837
+ }
838
+
839
+ def tryInOrder (cand1 : Candidate , cand2 : Candidate ) =
840
+ tryImplicit(cand1) match {
841
+ case success : SearchSuccess => success
842
+ case _ => tryImplicit(cand2)
843
+ }
844
+
845
+ def compareCandidate (prev : SearchSuccess , ref : TermRef , level : Int ): Int =
846
+ if (prev.ref eq ref) 0
847
+ else ctx.typerState.test(compare(prev.ref, ref, prev.level, level)(nestedContext()))
848
+
849
+ def disambiguate (alt1 : SearchResult , alt2 : SearchSuccess ) = alt1 match {
850
+ case alt1 : SearchSuccess =>
851
+ def isNumeric (tp : Type ) = tp.typeSymbol.isNumericValueClass
852
+ def isProperSubType (tp1 : Type , tp2 : Type ) =
853
+ tp1.isValueSubType(tp2) && ! tp2.isValueSubType(tp1)
854
+ val rpt = pt.resultType
855
+ val rt1 = alt1.ref.widen.resultType
856
+ val rt2 = alt2.ref.widen.resultType
857
+ def ambi = SearchFailure (new AmbiguousImplicits (alt1, alt2, pt, argument))
858
+ val diff = compareCandidate(alt1, alt2.ref, alt2.level)
859
+ assert(diff <= 0 )
860
+ if (diff < 0 ) alt2
861
+ else if (isNumeric(rpt) && isNumeric(rt1) && isNumeric(rt2))
862
+ if (isProperSubType(rt1, rt2)) alt2
863
+ else if (isProperSubType(rt2, rt1)) alt1
864
+ else ambi
865
+ else ambi
866
+ case _ : SearchFailure => alt2
867
+ }
868
+
869
+ def rank (pending : List [Candidate ], found : SearchResult , rfailures : List [SearchFailure ]): SearchResult = pending match {
870
+ case cand :: pending1 =>
871
+ tryImplicit(cand) match {
872
+ case fail : SearchFailure =>
873
+ if (fail.isAmbiguous) healAmbiguous(pending1, fail)
874
+ else rank(pending1, found, fail :: rfailures)
875
+ case best : SearchSuccess =>
876
+ if (ctx.mode.is(Mode .ImplicitExploration ) || isCoherent)
877
+ best
878
+ else disambiguate(found, best) match {
879
+ case retained : SearchSuccess =>
880
+ val newPending =
881
+ if (retained eq found) pending1
882
+ else pending1.filter(cand =>
883
+ compareCandidate(retained, cand.ref, cand.level) <= 0 )
884
+ rank(newPending, retained, rfailures)
885
+ case fail : SearchFailure =>
886
+ healAmbiguous(pending1, fail)
887
+ }
888
+ }
889
+ case nil =>
890
+ if (rfailures.isEmpty) found
891
+ else found.orElse(rfailures.reverse.maxBy(_.tree.treeSize))
892
+ }
893
+
894
+ def healAmbiguous (pending : List [Candidate ], fail : SearchFailure ) = {
895
+ val ambi = fail.reason.asInstanceOf [AmbiguousImplicits ]
896
+ val newPending = pending.filter(cand =>
897
+ compareCandidate(ambi.alt1, cand.ref, cand.level) < 0 &&
898
+ compareCandidate(ambi.alt2, cand.ref, cand.level) < 0 )
899
+ rank(newPending, fail, Nil ).orElse(fail)
900
+ }
901
+
902
+ def go : SearchResult = eligible match {
903
+ case Nil =>
904
+ SearchFailure (new NoMatchingImplicits (pt, argument))
905
+ case cand :: Nil =>
906
+ tryImplicit(cand)
907
+ case cand1 :: cand2 :: Nil =>
908
+ def compareTwo = cmpCandidates(cand1, cand2) match {
909
+ case 1 => tryInOrder(cand1, cand2)
910
+ case - 1 => tryInOrder(cand2, cand1)
911
+ case 0 =>
912
+ val alt1 = tryImplicit(cand1)
913
+ val alt2 = tryImplicit(cand2)
914
+ alt2 match {
915
+ case alt2 : SearchSuccess => disambiguate(alt1, alt2)
916
+ case _ =>
917
+ alt1 match {
918
+ case alt1 : SearchSuccess => alt1
919
+ case _ => if (alt1.tree.treeSize < alt2.tree.treeSize) alt2 else alt1
920
+ }
921
+ }
922
+ }
923
+ compareTwo
924
+ case _ =>
925
+ val cands = eligible.toArray
926
+ val pg = new PriorityGraph (cands, cmpCandidates)
927
+
928
+ def loop (found : SearchResult , rfailures : List [SearchFailure ]): SearchResult =
929
+ if (pg.hasNextSource())
930
+ tryImplicit(cands(pg.nextSource())) match {
931
+ case fail : SearchFailure =>
932
+ if (fail.isAmbiguous) fail
933
+ else {
934
+ pg.dropLastSource()
935
+ loop(found, fail :: rfailures)
936
+ }
937
+ case best : SearchSuccess =>
938
+ if (ctx.mode.is(Mode .ImplicitExploration ) || isCoherent)
939
+ best
940
+ else disambiguate(found, best) match {
941
+ case retained : SearchSuccess => loop(retained, rfailures)
942
+ case ambi => ambi
943
+ }
944
+ }
945
+ else if (found.isInstanceOf [SearchSuccess ]) found
946
+ else rfailures.reverse.maxBy(_.tree.treeSize)
947
+
948
+ loop(NoMatchingImplicitsFailure , Nil )
949
+ }
950
+
823
951
/** Given a list of implicit references, produce a list of search results,
824
952
* which is either a list of successes or a list of failures.
825
953
* - if one of the references produces an ambiguity error, return it
@@ -842,7 +970,7 @@ trait Implicits { self: Typer =>
842
970
if (history eq ctx.searchHistory)
843
971
SearchFailure (new DivergingImplicit (cand.ref, pt, argument))
844
972
else
845
- typedImplicit(cand)(nestedContext.setNewTyperState().setSearchHistory(history))
973
+ typedImplicit(cand)(nestedContext() .setNewTyperState().setSearchHistory(history))
846
974
result match {
847
975
case fail : SearchFailure =>
848
976
if (fail.isAmbiguous) fail :: Nil
@@ -852,7 +980,7 @@ trait Implicits { self: Typer =>
852
980
best :: Nil
853
981
else {
854
982
val newPending = pending1.filter(cand1 =>
855
- ctx.typerState.test(isAsGood(cand1.ref, best.ref, cand1.level, best.level)(nestedContext)))
983
+ ctx.typerState.test(isAsGood(cand1.ref, best.ref, cand1.level, best.level)(nestedContext() )))
856
984
rankImplicits(newPending, best :: successes, rfailures)
857
985
}
858
986
}
@@ -901,7 +1029,7 @@ trait Implicits { self: Typer =>
901
1029
case eliminated : SearchSuccess =>
902
1030
condense(results.filter(_ ne eliminated))
903
1031
case _ =>
904
- SearchFailure (new AmbiguousImplicits (best.ref , alt.ref , pt, argument))
1032
+ SearchFailure (new AmbiguousImplicits (best, alt, pt, argument))
905
1033
}
906
1034
case None =>
907
1035
ctx.runInfo.useCount(best.ref) += 1
@@ -920,21 +1048,20 @@ trait Implicits { self: Typer =>
920
1048
* `cand1` has been selected as an implicit more often than `cand2`.
921
1049
*/
922
1050
def prefer (cand1 : Candidate , cand2 : Candidate ): Boolean = {
923
- /* val sym1 = cand1.ref.symbol
1051
+ val level1 = cand1.level
1052
+ val level2 = cand2.level
1053
+ if (level1 > level2) return true
1054
+ if (level1 < level2) return false
1055
+ val sym1 = cand1.ref.symbol
924
1056
val sym2 = cand2.ref.symbol
925
- if (sym1.associatedFile == sym2.associatedFile) {
926
- val coord1 = sym1.coord
927
- val coord2 = sym2.coord
928
- if (coord1.isPosition && coord2.isPosition) {
929
- val pos1 = coord1.toPosition
930
- val pos2 = coord2.toPosition
931
- return pos1.exists && (!pos2.exists || pos1.start < pos2.start)
932
- }
933
- if (coord1.isIndex && coord2.isIndex)
934
- return coord1.toIndex < coord2.toIndex
935
- }
936
- */
937
- ranking(cand1) < ranking(cand2)
1057
+ val ownerScore = compareOwner(sym1.maybeOwner, sym2.maybeOwner)
1058
+ if (ownerScore > 0 ) return true
1059
+ if (ownerScore < 0 ) return false
1060
+ val arity1 = sym1.info.firstParamTypes.length
1061
+ val arity2 = sym2.info.firstParamTypes.length
1062
+ if (arity1 < arity2) return true
1063
+ if (arity1 > arity2) return false
1064
+ false
938
1065
}
939
1066
940
1067
/** Sort list of implicit references according to their popularity
@@ -946,10 +1073,13 @@ trait Implicits { self: Typer =>
946
1073
case e1 :: e2 :: Nil =>
947
1074
if (prefer(e2, e1)) e2 :: e1 :: Nil
948
1075
else eligible
949
- case _ => eligible.sortWith(prefer)
1076
+ case _ =>
1077
+ eligible.sortWith(prefer)
950
1078
}
951
1079
952
- condense(rankImplicits(sort(eligible), Nil , Nil ))
1080
+ if (true ) rank(sort(eligible), NoMatchingImplicitsFailure , Nil )
1081
+ else if (true ) go
1082
+ else condense(rankImplicits(sort(eligible), Nil , Nil ))
953
1083
}
954
1084
955
1085
/** Find a unique best implicit reference */
0 commit comments